From a7bcfd30da723f8f013e6da19da2dc98194ce65f Mon Sep 17 00:00:00 2001 From: Kar Rui Lau Date: Mon, 16 Sep 2024 11:54:33 +0800 Subject: [PATCH 1/2] feat: fix Input prefill style regression, add `isPrefilled` prop to Datepicker and Select (#754) This PR fixes the regression where `isPrefilled` state started to do nothing on the `Input` component due to downstream ChakraUI changes on their Input component. This PR also adds the `isPrefilled` prop to the following components: - `SingleSelect` - `MultiSelect` - `DatePicker` - `DateRangePicker` The prefilled styling follows the styling of the `Input` component. See storybook for details. --- react/src/DatePicker/DatePicker.stories.tsx | 5 ++++ react/src/DatePicker/DatePickerContext.tsx | 2 ++ .../DatePicker/components/DatePickerInput.tsx | 3 ++ react/src/DatePicker/types.ts | 4 +++ .../DateRangePicker.stories.tsx | 6 ++++ .../DateRangePickerContext.tsx | 2 ++ react/src/Input/Input.tsx | 8 +++++- react/src/MultiSelect/MultiSelect.stories.tsx | 6 ++++ react/src/MultiSelect/MultiSelectProvider.tsx | 6 ++++ .../src/SingleSelect/SingleSelect.stories.tsx | 6 ++++ .../src/SingleSelect/SingleSelectProvider.tsx | 6 ++++ react/src/theme/components/DatePicker.ts | 28 +++++++++++++------ react/src/theme/components/DateRangePicker.ts | 9 +++--- 13 files changed, 78 insertions(+), 13 deletions(-) diff --git a/react/src/DatePicker/DatePicker.stories.tsx b/react/src/DatePicker/DatePicker.stories.tsx index d659083b..9a16e0d8 100644 --- a/react/src/DatePicker/DatePicker.stories.tsx +++ b/react/src/DatePicker/DatePicker.stories.tsx @@ -62,6 +62,11 @@ FixedHeightCalendarPopover.args = { isCalendarFixedHeight: true, } +export const Prefilled = Template.bind({}) +Prefilled.args = { + isPrefilled: true, +} + export const SizeSmall = Template.bind({}) SizeSmall.args = { size: 'sm', diff --git a/react/src/DatePicker/DatePickerContext.tsx b/react/src/DatePicker/DatePickerContext.tsx index a2f355f3..09a7f148 100644 --- a/react/src/DatePicker/DatePickerContext.tsx +++ b/react/src/DatePicker/DatePickerContext.tsx @@ -103,6 +103,7 @@ const useProvideDatePicker = ({ isReadOnly: isReadOnlyProp, isRequired: isRequiredProp, isInvalid: isInvalidProp, + isPrefilled, locale, allowManualInput = true, allowInvalidDates = true, @@ -260,6 +261,7 @@ const useProvideDatePicker = ({ const styles = useMultiStyleConfig('DatePicker', { size, colorScheme, + isPrefilled, }) const placeholder = useMemo( diff --git a/react/src/DatePicker/components/DatePickerInput.tsx b/react/src/DatePicker/components/DatePickerInput.tsx index b89f95ce..6742c0f6 100644 --- a/react/src/DatePicker/components/DatePickerInput.tsx +++ b/react/src/DatePicker/components/DatePickerInput.tsx @@ -30,6 +30,7 @@ export const DatePickerInput = forwardRef<{}, 'input'>((_props, ref) => { size, inputPattern, innerRef, + styles, } = useDatePicker() const mergedInputRef = useMergeRefs(inputRef, innerRef, ref) @@ -71,6 +72,7 @@ export const DatePickerInput = forwardRef<{}, 'input'>((_props, ref) => { onBlur={handleInputBlur} onClick={handleInputClick} isReadOnly={fcProps.isReadOnly || !allowManualInput} + sx={styles.field} /> ) : ( ((_props, ref) => { inputMode="numeric" placeholder={placeholder} pattern={inputPattern} + sx={styles.field} /> )} diff --git a/react/src/DatePicker/types.ts b/react/src/DatePicker/types.ts index f67917a1..8e55fdf4 100644 --- a/react/src/DatePicker/types.ts +++ b/react/src/DatePicker/types.ts @@ -24,6 +24,10 @@ export interface DatePickerBaseProps * @defaultValue `true` */ allowInvalidDates?: boolean + /** + * Whether the input is in a prefilled state. + */ + isPrefilled?: boolean /** * Whether the calendar will close once a date is selected. * @defaultValue `true` diff --git a/react/src/DateRangePicker/DateRangePicker.stories.tsx b/react/src/DateRangePicker/DateRangePicker.stories.tsx index f06645f8..380aa18c 100644 --- a/react/src/DateRangePicker/DateRangePicker.stories.tsx +++ b/react/src/DateRangePicker/DateRangePicker.stories.tsx @@ -57,6 +57,12 @@ DatePickerDisabled.args = { defaultValue: [new Date('2001-01-01'), new Date('2002-01-03')], } +export const Prefilled = Template.bind({}) +Prefilled.args = { + isPrefilled: true, + defaultValue: [new Date('2001-01-01'), new Date('2002-01-03')], +} + export const Mobile = Template.bind({}) Mobile.parameters = getMobileViewParameters() diff --git a/react/src/DateRangePicker/DateRangePickerContext.tsx b/react/src/DateRangePicker/DateRangePickerContext.tsx index 68605052..ca31bb5e 100644 --- a/react/src/DateRangePicker/DateRangePickerContext.tsx +++ b/react/src/DateRangePicker/DateRangePickerContext.tsx @@ -103,6 +103,7 @@ const useProvideDateRangePicker = ({ isReadOnly: isReadOnlyProp, isRequired: isRequiredProp, isInvalid: isInvalidProp, + isPrefilled, locale, allowManualInput = true, allowInvalidDates = true, @@ -333,6 +334,7 @@ const useProvideDateRangePicker = ({ const styles = useMultiStyleConfig('DateRangePicker', { size, colorScheme, + isPrefilled, }) const placeholder = useMemo( diff --git a/react/src/Input/Input.tsx b/react/src/Input/Input.tsx index 8ab4910a..c6995222 100644 --- a/react/src/Input/Input.tsx +++ b/react/src/Input/Input.tsx @@ -32,7 +32,13 @@ export const Input = forwardRef((props, ref) => { // Return normal input component if not success state. if (!props.isSuccess) { - return + return ( + + ) } return ( diff --git a/react/src/MultiSelect/MultiSelect.stories.tsx b/react/src/MultiSelect/MultiSelect.stories.tsx index d30132f5..78b56e18 100644 --- a/react/src/MultiSelect/MultiSelect.stories.tsx +++ b/react/src/MultiSelect/MultiSelect.stories.tsx @@ -103,6 +103,12 @@ DisabledWithSelection.args = { values: ['What happens when the label is fairly long', 'Bat'], } +export const Prefilled = Template.bind({}) +Prefilled.args = { + isPrefilled: true, + values: ['What happens when the label is fairly long', 'Bat'], +} + export const WithFixedItemHeight = Template.bind({}) WithFixedItemHeight.args = { size: 'md', diff --git a/react/src/MultiSelect/MultiSelectProvider.tsx b/react/src/MultiSelect/MultiSelectProvider.tsx index 9c7c1719..bcb43d56 100644 --- a/react/src/MultiSelect/MultiSelectProvider.tsx +++ b/react/src/MultiSelect/MultiSelectProvider.tsx @@ -70,6 +70,10 @@ export interface MultiSelectProviderProps< * If `true`, the selected items will take up the full width of the input container. Defaults to `false`. */ isStretchLayout?: boolean + /** + * Whether the input is in a prefilled state. + */ + isPrefilled?: boolean } export const MultiSelectProvider = ({ items: rawItems, @@ -81,6 +85,7 @@ export const MultiSelectProvider = ({ placeholder: placeholderProp, clearButtonLabel = 'Clear selection', isSearchable = true, + isPrefilled = false, defaultIsOpen, isInvalid: isInvalidProp, isReadOnly: isReadOnlyProp, @@ -297,6 +302,7 @@ export const MultiSelectProvider = ({ size, isFocused: isFocused || isOpen, isEmpty: selectedItems.length === 0, + isPrefilled, }) const virtualListHeight = useMemo(() => { diff --git a/react/src/SingleSelect/SingleSelect.stories.tsx b/react/src/SingleSelect/SingleSelect.stories.tsx index d677a27e..9b28fe48 100644 --- a/react/src/SingleSelect/SingleSelect.stories.tsx +++ b/react/src/SingleSelect/SingleSelect.stories.tsx @@ -180,6 +180,12 @@ Disabled.args = { isDisabled: true, } +export const Prefilled = Template.bind({}) +Prefilled.args = { + isPrefilled: true, + value: itemToValue(INITIAL_COMBOBOX_ITEMS[2]), +} + export const FormInput: StoryFn = (args) => { const [value, setValue] = useState(args.value) diff --git a/react/src/SingleSelect/SingleSelectProvider.tsx b/react/src/SingleSelect/SingleSelectProvider.tsx index 062eec53..3550dfae 100644 --- a/react/src/SingleSelect/SingleSelectProvider.tsx +++ b/react/src/SingleSelect/SingleSelectProvider.tsx @@ -39,6 +39,10 @@ export interface SingleSelectProviderProps< /** Color scheme of component */ colorScheme?: ThemingProps<'SingleSelect'>['colorScheme'] fixedItemHeight?: number + /** + * Whether the input is in a prefilled state. + */ + isPrefilled?: boolean } export const SingleSelectProvider = ({ items: rawItems, @@ -51,6 +55,7 @@ export const SingleSelectProvider = ({ clearButtonLabel = 'Clear selection', isClearable = true, isSearchable = true, + isPrefilled = false, initialIsOpen, isInvalid: isInvalidProp, isReadOnly: isReadOnlyProp, @@ -234,6 +239,7 @@ export const SingleSelectProvider = ({ const styles = useMultiStyleConfig('SingleSelect', { size, isClearable, + isPrefilled, colorScheme, }) diff --git a/react/src/theme/components/DatePicker.ts b/react/src/theme/components/DatePicker.ts index 9067377b..4d0992df 100644 --- a/react/src/theme/components/DatePicker.ts +++ b/react/src/theme/components/DatePicker.ts @@ -1,40 +1,52 @@ import { createMultiStyleConfigHelpers } from '@chakra-ui/react' import { anatomy } from '@chakra-ui/theme-tools' import { memoizedGet as get } from '@chakra-ui/utils' +import omit from 'lodash/omit' + +import { Input } from './Input' export const datepickerAnatomy = anatomy('datepicker').parts( 'header', 'inputButton', 'container', 'calendarButton', + 'field', ) const { defineMultiStyleConfig, definePartsStyle } = createMultiStyleConfigHelpers(datepickerAnatomy.keys) const sizes = { - xs: definePartsStyle({ + xs: definePartsStyle((props) => ({ + field: Input.sizes?.xs(props).field, inputButton: { fontSize: '1rem', }, - }), - sm: definePartsStyle({ + })), + sm: definePartsStyle((props) => ({ + field: Input.sizes?.sm(props).field, inputButton: { fontSize: '1.25rem', }, - }), - md: definePartsStyle({ + })), + md: definePartsStyle((props) => ({ + field: Input.sizes?.md(props).field, inputButton: { fontSize: '1.25rem', }, - }), + })), } -const baseStyle = definePartsStyle(({ theme }) => { - const themeTextStyles = get(theme, 'textStyles') +const baseStyle = definePartsStyle((props) => { + const themeTextStyles = get(props.theme, 'textStyles') + const inputFieldStyle = omit( + Input.variants?.outline(props).field, + 'borderRadius', + ) return { container: { bg: 'utility.ui', }, + field: inputFieldStyle, calendarButton: { _active: { zIndex: '1', diff --git a/react/src/theme/components/DateRangePicker.ts b/react/src/theme/components/DateRangePicker.ts index a6c4c1dc..136cb088 100644 --- a/react/src/theme/components/DateRangePicker.ts +++ b/react/src/theme/components/DateRangePicker.ts @@ -1,4 +1,5 @@ import { createMultiStyleConfigHelpers } from '@chakra-ui/react' +import omit from 'lodash/omit' import { DatePicker, datepickerAnatomy } from './DatePicker' import { Input } from './Input' @@ -40,19 +41,19 @@ const sizes = { xs: definePartsStyle((props) => { return { fieldwrapper: Input.sizes?.xs(props).field, - inputButton: DatePicker.sizes?.xs.inputButton, + inputButton: DatePicker.sizes?.xs(props).inputButton, } }), sm: definePartsStyle((props) => { return { fieldwrapper: Input.sizes?.sm(props).field, - inputButton: DatePicker.sizes?.sm.inputButton, + inputButton: DatePicker.sizes?.sm(props).inputButton, } }), md: definePartsStyle((props) => { return { fieldwrapper: Input.sizes?.md(props).field, - inputButton: DatePicker.sizes?.md.inputButton, + inputButton: DatePicker.sizes?.md(props).inputButton, } }), } @@ -64,7 +65,7 @@ const variants = { export const DateRangePicker = defineMultiStyleConfig({ variants, sizes, - baseStyle: DatePicker.baseStyle, + baseStyle: (props) => omit(DatePicker.baseStyle?.(props), 'field'), defaultProps: { variant: 'outline', size: 'md', From 06e23c916f9ccf47da599034fce4b96a315535e1 Mon Sep 17 00:00:00 2001 From: Kar Rui Lau Date: Mon, 16 Sep 2024 11:55:54 +0800 Subject: [PATCH 2/2] build: release v1.24.0 --- react/CHANGELOG.md | 9 +++++++++ react/package-lock.json | 4 ++-- react/package.json | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/react/CHANGELOG.md b/react/CHANGELOG.md index eb950355..c49c3655 100644 --- a/react/CHANGELOG.md +++ b/react/CHANGELOG.md @@ -4,12 +4,21 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [v1.24.0](https://github.com/opengovsg/design-system/compare/v1.23.0...v1.24.0) + +- feat: fix Input prefill style regression, add `isPrefilled` prop to Datepicker and Select [`#754`](https://github.com/opengovsg/design-system/pull/754) +- chore: merge v1.23.0 back to main [`#747`](https://github.com/opengovsg/design-system/pull/747) +- build: release v1.23.0 [`#746`](https://github.com/opengovsg/design-system/pull/746) + #### [v1.23.0](https://github.com/opengovsg/design-system/compare/v1.22.0...v1.23.0) +> 20 August 2024 + - fix!(DatePicker): add `renderInputElement` prop [`#745`](https://github.com/opengovsg/design-system/pull/745) - chore: enable dual publish to GitHub [`#740`](https://github.com/opengovsg/design-system/pull/740) - chore: merge latest v1.22.0 back to main [`#732`](https://github.com/opengovsg/design-system/pull/732) - build: release v1.22.0 [`#731`](https://github.com/opengovsg/design-system/pull/731) +- build: release v1.23.0 [`224b03c`](https://github.com/opengovsg/design-system/commit/224b03c263672eccb9a409309b4c549cb718b6f2) #### [v1.22.0](https://github.com/opengovsg/design-system/compare/v1.21.0...v1.22.0) diff --git a/react/package-lock.json b/react/package-lock.json index 78713715..3ecbd217 100644 --- a/react/package-lock.json +++ b/react/package-lock.json @@ -1,12 +1,12 @@ { "name": "@opengovsg/design-system-react", - "version": "1.23.0", + "version": "1.24.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@opengovsg/design-system-react", - "version": "1.23.0", + "version": "1.24.0", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "@chakra-ui/utils": "^2.0.14", diff --git a/react/package.json b/react/package.json index 84a45393..89fc4d9f 100644 --- a/react/package.json +++ b/react/package.json @@ -1,6 +1,6 @@ { "name": "@opengovsg/design-system-react", - "version": "1.23.0", + "version": "1.24.0", "description": "React components", "homepage": "https://github.com/opengovsg/design-system/react#readme", "bugs": "https://github.com/opengovsg/design-system/issues",