diff --git a/client/common/Button.tsx b/client/common/Button.tsx index d52abf3140..9b31c4acd1 100644 --- a/client/common/Button.tsx +++ b/client/common/Button.tsx @@ -92,6 +92,7 @@ export interface ButtonProps extends React.HTMLAttributes { * but React will automatically convert a boolean prop to the correct string value. */ focusable?: boolean; + label?: string; } interface StyledButtonProps extends ButtonProps { diff --git a/client/common/icons.jsx b/client/common/icons.tsx similarity index 81% rename from client/common/icons.jsx rename to client/common/icons.tsx index bd5a673e2a..80411bf937 100644 --- a/client/common/icons.jsx +++ b/client/common/icons.tsx @@ -26,13 +26,25 @@ import Filter from '../images/filter.svg'; import Cross from '../images/cross.svg'; import Copy from '../images/copy.svg'; +export interface IconColors { + default?: string; + hover?: string; +} + +export interface IconProps extends React.SVGProps { + 'aria-label'?: string; + Icon?: IconColors; +} + // HOC that adds the right web accessibility props // https://www.scottohara.me/blog/2019/05/22/contextual-images-svgs-and-a11y.html // could also give these a default size, color, etc. based on the theme // Need to add size to these - like small icon, medium icon, large icon. etc. -function withLabel(SvgComponent) { - const StyledIcon = styled(SvgComponent)` +function withLabel( + SvgComponent: React.ComponentType> +) { + const StyledIcon = styled(SvgComponent)` &&& { color: ${(props) => props.Icon?.default}; & g, @@ -53,27 +65,27 @@ function withLabel(SvgComponent) { } `; - const Icon = (props) => { - const { 'aria-label': ariaLabel } = props; + // Necessary because styled components inject a different type for the ref prop + type StyledIconProps = Omit< + React.ComponentProps, + 'ref' + > & { + ref?: React.Ref; + }; + + const Icon = (props: StyledIconProps) => { + const { 'aria-label': ariaLabel, ...rest } = props; if (ariaLabel) { return ( ); } - return ; - }; - - Icon.propTypes = { - 'aria-label': PropTypes.string - }; - - Icon.defaultProps = { - 'aria-label': null + return ; }; return Icon; diff --git a/client/common/useSyncFormTranslations.ts b/client/common/useSyncFormTranslations.ts index 4a90362750..c116db1e04 100644 --- a/client/common/useSyncFormTranslations.ts +++ b/client/common/useSyncFormTranslations.ts @@ -1,30 +1,32 @@ import { useEffect, MutableRefObject } from 'react'; +import type { FormApi } from 'final-form'; -export interface FormLike { - getState(): { values: Record }; - reset(): void; - change(field: string, value: unknown): void; -} +// Generic FormLike that mirrors FormApi for any form value type +export type FormLike> = Pick< + FormApi, + 'getState' | 'reset' | 'change' +>; /** * This hook ensures that form values are preserved when the language changes. * @param formRef * @param language */ -export const useSyncFormTranslations = ( - formRef: MutableRefObject, +export const useSyncFormTranslations = >( + formRef: MutableRefObject | null>, language: string ) => { useEffect(() => { - const form = formRef.current; + const form = formRef?.current; if (!form) return; const { values } = form.getState(); form.reset(); - Object.keys(values).forEach((field) => { - if (values[field]) { - form.change(field, values[field]); + (Object.keys(values) as (keyof FormValues)[]).forEach((field) => { + const value = values[field]; + if (value !== undefined && value !== null && value !== '') { + form.change(field, value); } }); }, [language]); diff --git a/client/modules/App/App.jsx b/client/modules/App/App.jsx index 53b0c82c63..242b5244c8 100644 --- a/client/modules/App/App.jsx +++ b/client/modules/App/App.jsx @@ -6,7 +6,7 @@ import { showReduxDevTools } from '../../store'; import DevTools from './components/DevTools'; import { setPreviousPath } from '../IDE/actions/ide'; import { setLanguage } from '../IDE/actions/preferences'; -import CookieConsent from '../User/components/CookieConsent'; +import { CookieConsent } from '../User/components/CookieConsent'; function hideCookieConsent(pathname) { if (pathname.includes('/full/') || pathname.includes('/embed/')) { diff --git a/client/modules/IDE/actions/preferences.ts b/client/modules/IDE/actions/preferences.ts index ccb5ef6a63..f626a20002 100644 --- a/client/modules/IDE/actions/preferences.ts +++ b/client/modules/IDE/actions/preferences.ts @@ -6,7 +6,6 @@ import type { UpdatePreferencesDispatch, SetPreferencesTabValue, SetFontSizeValue, - GetRootState, SetLineNumbersValue, SetAutocloseBracketsQuotesValue, SetAutocompleteHinterValue, @@ -20,6 +19,7 @@ import type { SetLanguageValue, SetThemeValue } from './preferences.types'; +import type { GetRootState } from '../../../reducers'; function updatePreferences( formParams: UpdatePreferencesRequestBody, diff --git a/client/modules/IDE/actions/preferences.types.ts b/client/modules/IDE/actions/preferences.types.ts index e54d28d3cd..dd56c65a77 100644 --- a/client/modules/IDE/actions/preferences.types.ts +++ b/client/modules/IDE/actions/preferences.types.ts @@ -1,6 +1,6 @@ import * as ActionTypes from '../../../constants'; import type { PreferencesState } from '../reducers/preferences'; -import type { RootState } from '../../../reducers'; +import type { GetRootState } from '../../../reducers'; // Value Definitions: export type SetPreferencesTabValue = PreferencesState['tabIndex']; @@ -112,5 +112,3 @@ export type PreferencesThunk = ( dispatch: UpdatePreferencesDispatch, getState: GetRootState ) => void; - -export type GetRootState = () => RootState; diff --git a/client/modules/IDE/components/Header/Toolbar.jsx b/client/modules/IDE/components/Header/Toolbar.jsx index fbf0b5d091..16ed74ff3e 100644 --- a/client/modules/IDE/components/Header/Toolbar.jsx +++ b/client/modules/IDE/components/Header/Toolbar.jsx @@ -20,7 +20,7 @@ import StopIcon from '../../../../images/stop.svg'; import PreferencesIcon from '../../../../images/preferences.svg'; import ProjectName from './ProjectName'; import VersionIndicator from '../VersionIndicator'; -import VisibilityDropdown from '../../../User/components/VisibilityDropdown'; +import { VisibilityDropdown } from '../../../User/components/VisibilityDropdown'; import { changeVisibility } from '../../actions/project'; const Toolbar = (props) => { diff --git a/client/modules/IDE/components/Header/__snapshots__/Nav.unit.test.jsx.snap b/client/modules/IDE/components/Header/__snapshots__/Nav.unit.test.jsx.snap index 02e4776b1c..382e49aadd 100644 --- a/client/modules/IDE/components/Header/__snapshots__/Nav.unit.test.jsx.snap +++ b/client/modules/IDE/components/Header/__snapshots__/Nav.unit.test.jsx.snap @@ -351,7 +351,7 @@ exports[`Nav renders dashboard version for mobile 1`] = ` >