Skip to content

Commit

Permalink
Finish Typescripterizing the frontend (badges#4026)
Browse files Browse the repository at this point in the history
* Typescripterize RequestMarkupButton

* Typescripterize Customizer

* Typescripterize MarkupModalContent (+ tweaks)

* More TypeScript

* More TS

* Fix build

* Remove prop-types dependency

* More types

* Update frontend/components/badge-examples.tsx

* Update frontend/components/badge-examples.tsx

* RequestMarkupButton: Fix weird formatting on click
  • Loading branch information
paulmelnikow authored and repo-ranger[bot] committed Sep 16, 2019
1 parent 69a57fa commit 6560706
Show file tree
Hide file tree
Showing 19 changed files with 270 additions and 233 deletions.
1 change: 0 additions & 1 deletion .eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,6 @@ rules:
'@typescript-eslint/camelcase': off

react/jsx-sort-props: 'error'
react/prop-types: 'off'
react-hooks/rules-of-hooks: 'error'
react-hooks/exhaustive-deps: 'error'
jsx-quotes: ['error', 'prefer-double']
2 changes: 1 addition & 1 deletion core/base-service/service-definitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const Joi = require('@hapi/joi')

// This should be kept in sync with the schema in
// `frontend/lib/service-definitions/service-definition-prop-types.js`.
// `frontend/lib/service-definitions/index.ts`.

const arrayOfStrings = Joi.array()
.items(Joi.string())
Expand Down
26 changes: 8 additions & 18 deletions frontend/components/badge-examples.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,13 @@ import {
} from '../../core/badge-urls/make-badge-url'
import { removeRegexpFromPattern } from '../lib/pattern-helpers'
import {
ExampleSignature,
Example as ExampleData,
Suggestion,
RenderableExample,
} from '../lib/service-definitions'
import { Badge } from './common'
import { StyledCode } from './snippet'

export interface SuggestionData {
title: string
link: string
example: ExampleSignature
preview: {
style?: string
}
}

type RenderableExampleData = ExampleData | SuggestionData

const ExampleTable = styled.table`
min-width: 50%;
margin: auto;
Expand All @@ -49,19 +39,19 @@ function Example({
isBadgeSuggestion,
}: {
baseUrl?: string
onClick: (exampleData: RenderableExampleData) => void
exampleData: RenderableExampleData
onClick: (example: RenderableExample, isSuggestion: boolean) => void
exampleData: RenderableExample
isBadgeSuggestion: boolean
}) {
function handleClick() {
onClick(exampleData)
onClick(exampleData, isBadgeSuggestion)
}

let exampleUrl, previewUrl
if (isBadgeSuggestion) {
const {
example: { pattern, namedParams, queryParams },
} = exampleData as SuggestionData
} = exampleData as Suggestion
exampleUrl = previewUrl = badgeUrlFromPattern({
baseUrl,
pattern,
Expand Down Expand Up @@ -112,10 +102,10 @@ export function BadgeExamples({
baseUrl,
onClick,
}: {
examples: RenderableExampleData[]
examples: RenderableExample[]
areBadgeSuggestions: boolean
baseUrl?: string
onClick: (exampleData: RenderableExampleData) => void
onClick: (exampleData: RenderableExample, isSuggestion: boolean) => void
}) {
return (
<ExampleTable>
Expand Down
8 changes: 5 additions & 3 deletions frontend/components/customizer/copied-content-indicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ function _CopiedContentIndicator(
{
copiedContent,
children,
}: { copiedContent: JSX.Element; children: JSX.Element | JSX.Element[] },
}: {
copiedContent: JSX.Element | string
children: JSX.Element | JSX.Element[]
},
ref: React.Ref<CopiedContentIndicatorHandle>
) {
const [pose, setPose] = useState('hidden')
Expand Down Expand Up @@ -64,5 +67,4 @@ function _CopiedContentIndicator(
</ContentAnchor>
)
}
const CopiedContentIndicator = forwardRef(_CopiedContentIndicator)
export default CopiedContentIndicator
export const CopiedContentIndicator = forwardRef(_CopiedContentIndicator)
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import React, { useRef, useState } from 'react'
import PropTypes from 'prop-types'
import clipboardCopy from 'clipboard-copy'
import { staticBadgeUrl } from '../../../core/badge-urls/make-badge-url'
import { generateMarkup } from '../../lib/generate-image-markup'
import { objectOfKeyValuesPropType } from '../../lib/service-definitions/service-definition-prop-types'
import { generateMarkup, MarkupFormat } from '../../lib/generate-image-markup'
import { Badge } from '../common'
import PathBuilder from './path-builder'
import QueryStringBuilder from './query-string-builder'
import RequestMarkupButtom from './request-markup-button'
import CopiedContentIndicator from './copied-content-indicator'
import {
CopiedContentIndicator,
CopiedContentIndicatorHandle,
} from './copied-content-indicator'

function getBaseUrlFromWindowLocation() {
function getBaseUrlFromWindowLocation(): string {
// Default to the current hostname for when there is no `BASE_URL` set
// at build time (as in most PaaS deploys).
const { protocol, hostname } = window.location
Expand All @@ -26,8 +27,21 @@ export default function Customizer({
initialStyle,
isPrefilled,
link = '',
}: {
baseUrl: string
title: string
pattern: string
exampleNamedParams: { [k: string]: string }
exampleQueryParams: { [k: string]: string }
initialStyle?: string
isPrefilled: boolean
link?: string
}) {
const indicatorRef = useRef()
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/35572
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/28884#issuecomment-471341041
const indicatorRef = useRef<
CopiedContentIndicatorHandle
>() as React.MutableRefObject<CopiedContentIndicatorHandle>
const [path, setPath] = useState('')
const [queryString, setQueryString] = useState()
const [pathIsComplete, setPathIsComplete] = useState()
Expand Down Expand Up @@ -61,7 +75,7 @@ export default function Customizer({
)
}

async function copyMarkup(markupFormat) {
async function copyMarkup(markupFormat: MarkupFormat) {
const builtBadgeUrl = generateBuiltBadgeUrl()
const markup = generateMarkup({
badgeUrl: builtBadgeUrl,
Expand All @@ -79,7 +93,9 @@ export default function Customizer({
}

setMarkup(markup)
indicatorRef.current.trigger()
if (indicatorRef.current) {
indicatorRef.current.trigger()
}
}

function renderMarkupAndLivePreview() {
Expand All @@ -102,12 +118,24 @@ export default function Customizer({
)
}

function handlePathChange({ path, isComplete }) {
function handlePathChange({
path,
isComplete,
}: {
path: string
isComplete: boolean
}) {
setPath(path)
setPathIsComplete(isComplete)
}

function handleQueryStringChange({ queryString, isComplete }) {
function handleQueryStringChange({
queryString,
isComplete,
}: {
queryString: string
isComplete: boolean
}) {
setQueryString(queryString)
}

Expand All @@ -128,13 +156,3 @@ export default function Customizer({
</form>
)
}
Customizer.propTypes = {
baseUrl: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
pattern: PropTypes.string.isRequired,
exampleNamedParams: objectOfKeyValuesPropType,
exampleQueryParams: objectOfKeyValuesPropType,
initialStyle: PropTypes.string,
isPrefilled: PropTypes.bool,
link: PropTypes.string,
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import React, { useRef } from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import Select, { components } from 'react-select'
import { MarkupFormat } from '../../lib/generate-image-markup'

const ClickableControl = props => (
const ClickableControl = (props: any) => (
<components.Control
{...props}
innerProps={{
onMouseDown: props.selectProps.onControlMouseDown,
}}
/>
)
ClickableControl.propTypes = {
selectProps: PropTypes.object.isRequired,

interface Option {
value: MarkupFormat
label: string
}

const MarkupFormatSelect = styled(Select)`
Expand Down Expand Up @@ -61,30 +63,49 @@ const MarkupFormatSelect = styled(Select)`
}
`

const markupOptions = [
const markupOptions: Option[] = [
{ value: 'markdown', label: 'Copy Markdown' },
{ value: 'rst', label: 'Copy reStructuredText' },
{ value: 'asciidoc', label: 'Copy AsciiDoc' },
{ value: 'html', label: 'Copy HTML' },
]

export default function GetMarkupButton({ onMarkupRequested, isDisabled }) {
const selectRef = useRef()

async function onControlMouseDown(event) {
export default function GetMarkupButton({
onMarkupRequested,
isDisabled,
}: {
onMarkupRequested: (markupFormat: MarkupFormat) => Promise<void>
isDisabled: boolean
}) {
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/35572
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/28884#issuecomment-471341041
const selectRef = useRef<Select<Option>>() as React.MutableRefObject<
Select<Option>
>

async function onControlMouseDown(event: MouseEvent) {
if (onMarkupRequested) {
await onMarkupRequested('link')
}
selectRef.current.blur()
if (selectRef.current) {
selectRef.current.blur()
}
}

async function onOptionClick({ value: markupFormat }) {
async function onOptionClick(
// Eeesh.
value: Option | readonly Option[] | null | undefined
): Promise<void> {
const { value: markupFormat } = value as Option
if (onMarkupRequested) {
await onMarkupRequested(markupFormat)
}
}

return (
// TODO It doesn't seem to be possible to check the types and wrap with
// styled-components at the same time. To check the types, replace
// `MarkupFormatSelect` with `Select<Option>`.
<MarkupFormatSelect
blurInputOnSelect
classNamePrefix="markup-format"
Expand All @@ -98,11 +119,7 @@ export default function GetMarkupButton({ onMarkupRequested, isDisabled }) {
options={markupOptions}
placeholder="Copy Badge URL"
ref={selectRef}
value=""
value={null}
/>
)
}
GetMarkupButton.propTypes = {
onMarkupRequested: PropTypes.func.isRequired,
isDisabled: PropTypes.bool,
}
Loading

0 comments on commit 6560706

Please sign in to comment.