diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 6b01d991ffb277..f8233dc3cb3c0d 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -5,6 +5,7 @@ ### Breaking Changes - Components that compose Emotion style fragments with `cx()` should pass source-order-dependent fragments in a single `css()` call. Passing separate fragments can change override order after the following components stopped rendering styles through Emotion: + - `SelectControl` ([#84732](https://github.com/WordPress/gutenberg/pull/84732)) - `DatePicker` ([#84724](https://github.com/WordPress/gutenberg/pull/84724)) - `FocalPointPicker` ([#84736](https://github.com/WordPress/gutenberg/pull/84736)) - `Menu` ([#84706](https://github.com/WordPress/gutenberg/pull/84706)) @@ -35,6 +36,7 @@ - `StyleProvider`: Register module styles before children measure layout in iframe documents ([#84736](https://github.com/WordPress/gutenberg/pull/84736)). - `BorderBoxControl`: Unpack multi-value CSS box shorthand strings (such as `"0 0 0 2px"`) into individual side widths when splitting borders ([#84826](https://github.com/WordPress/gutenberg/pull/84826)). +- `SelectControl`: Preserve the minimal variant's inline layout in iframe documents ([#84732](https://github.com/WordPress/gutenberg/pull/84732)). - `DatePicker`: Keep month navigation in a grid in iframe documents ([#84724](https://github.com/WordPress/gutenberg/pull/84724)). - `PaletteEdit`: Name picker dialogs and add screen-reader-only dismissal controls while preserving live edits ([#84333](https://github.com/WordPress/gutenberg/pull/84333)). - `Grid`: Fix the `bottomLeft` alignment, which resolved to the top right corner instead of the bottom left one ([#82641](https://github.com/WordPress/gutenberg/pull/82641)). diff --git a/packages/components/src/select-control/chevron-down.tsx b/packages/components/src/select-control/chevron-down.tsx index 1c22c59a25e551..40c9580b9b97eb 100644 --- a/packages/components/src/select-control/chevron-down.tsx +++ b/packages/components/src/select-control/chevron-down.tsx @@ -1,17 +1,20 @@ +import clsx from 'clsx'; +import { isRTL } from '@wordpress/i18n'; import { chevronDown, Icon } from '@wordpress/icons'; -import { - chevronIconSize, - DownArrowWrapper, - InputControlSuffixWrapperWithClickThrough, -} from './styles/select-control-styles'; +import InputControlSuffixWrapper from '../input-control/input-suffix-wrapper'; +import styles from './style.module.scss'; const SelectControlChevronDown = () => { return ( - - - - - + +
+ +
+
); }; diff --git a/packages/components/src/select-control/index.tsx b/packages/components/src/select-control/index.tsx index f01d8f02a9b27f..b7458d4bf2b89f 100644 --- a/packages/components/src/select-control/index.tsx +++ b/packages/components/src/select-control/index.tsx @@ -1,8 +1,11 @@ import clsx from 'clsx'; import { useInstanceId } from '@wordpress/compose'; import { forwardRef } from '@wordpress/element'; +import { isRTL } from '@wordpress/i18n'; import BaseControl from '../base-control'; -import { Select, StyledInputBase } from './styles/select-control-styles'; +import InputBase from '../input-control/input-base'; +import { PolymorphicElement } from '../utils/polymorphic-element'; +import styles from './style.module.scss'; import type { WordPressComponentProps } from '../context'; import type { SelectControlProps } from './types'; import SelectControlChevronDown from './chevron-down'; @@ -85,7 +88,7 @@ function UnforwardedSelectControl< V extends string >( return ( - ( __unstableInputWidth={ variant === 'minimal' ? 'auto' : undefined } - variant={ variant } + className={ clsx( styles.root, { + [ styles[ 'is-disabled' ] ]: disabled, + [ styles[ 'is-minimal' ] ]: variant === 'minimal', + } ) } > - - + + ); } diff --git a/packages/components/src/select-control/style.module.scss b/packages/components/src/select-control/style.module.scss new file mode 100644 index 00000000000000..c289a28fd3fb97 --- /dev/null +++ b/packages/components/src/select-control/style.module.scss @@ -0,0 +1,100 @@ +@use "../utils/theme-variables" as *; + +.root { + color: $components-color-foreground; + /* stylelint-disable-next-line declaration-property-value-disallowed-list -- Preserve the existing pointer cursor during the migration. */ + cursor: pointer; + + &:where(.is-disabled) { + color: var(--wpds-color-foreground-interactive-neutral-disabled); + cursor: default; + } +} + +.root.root:where(.is-minimal) { + // Keep the minimal layout over Flex in either stylesheet order. + display: inline-flex; +} + +.input.input.input { + // Match the existing specificity over wp-admin's form styles. + --_wp-components-select-control-padding: 12px; + appearance: none; + background: transparent; + box-sizing: border-box; + border: none; + box-shadow: none !important; + color: currentColor; + cursor: inherit; + display: block; + font-family: inherit; + font-size: 16px; + line-height: 1.3; + margin: 0; + width: 100%; + max-width: none; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + + @media (min-width: 600px) { + font-size: 13px; + } + + &:where(.is-small, .is-compact) { + --_wp-components-select-control-padding: 8px; + } + + &:where(:not(.is-multiple)) { + height: 40px; + min-height: 40px; + padding-block: 0; + + &:where(.is-small) { + height: 24px; + min-height: 24px; + } + + &:where(.is-compact) { + height: 32px; + min-height: 32px; + } + } + + &:where(.is-multiple) { + // Preserve native height for multiple selection. + overflow: auto; + padding-block: var(--_wp-components-select-control-padding); + } + + /* stylelint-disable logical-css/require-logical-properties -- The native select can override its direction independently of the sibling chevron; reserve space on the same physical side. */ + padding-left: var(--_wp-components-select-control-padding); + padding-right: calc(var(--_wp-components-select-control-padding) + 18px); + &:where(.is-rtl) { + padding-left: calc(var(--_wp-components-select-control-padding) + 18px); + padding-right: var(--_wp-components-select-control-padding); + } + + &:where(.is-minimal) { + field-sizing: content; + } +} +/* stylelint-enable logical-css/require-logical-properties */ + +.down-arrow { + margin-inline-end: -4px; + line-height: 0; +} + +.suffix { + position: absolute; + pointer-events: none; + + /* stylelint-disable logical-css/require-logical-properties -- Match the locale-based chevron position. */ + right: 0; + &:where(.is-rtl) { + right: auto; + left: 0; + } +} +/* stylelint-enable logical-css/require-logical-properties */ diff --git a/packages/components/src/select-control/styles/select-control-styles.ts b/packages/components/src/select-control/styles/select-control-styles.ts deleted file mode 100644 index acc8c4f5a32a34..00000000000000 --- a/packages/components/src/select-control/styles/select-control-styles.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { css } from '@emotion/react'; -import styled from '@emotion/styled'; -import { COLORS, rtl, CONFIG } from '../../utils'; -import { space } from '../../utils/space'; -import type { SelectControlProps } from '../types'; -import InputControlSuffixWrapper from '../../input-control/input-suffix-wrapper'; -import { fontSizeStyles } from '../../input-control/styles/input-control-styles'; -import InputBase from '../../input-control/input-base'; - -interface SelectProps extends Pick< - SelectControlProps, - 'disabled' | 'multiple' | 'variant' -> { - // Using `selectSize` instead of `size` to avoid a type conflict with the - // `size` HTML attribute of the `select` element. - selectSize?: SelectControlProps[ 'size' ]; -} - -const disabledStyles = ( { disabled }: SelectProps ) => { - if ( ! disabled ) { - return ''; - } - - return css` - color: ${ COLORS.ui.textDisabled }; - cursor: default; - `; -}; - -const inputBaseVariantStyles = ( { variant }: SelectProps ) => { - if ( variant === 'minimal' ) { - return css` - display: inline-flex; - `; - } - - return ''; -}; - -export const StyledInputBase = styled( InputBase )` - color: ${ COLORS.theme.foreground }; - cursor: pointer; - - ${ disabledStyles } - ${ inputBaseVariantStyles } -`; - -const sizeStyles = ( { multiple, selectSize = 'default' }: SelectProps ) => { - if ( multiple ) { - // When `multiple`, just use the native browser styles - // without setting explicit height. - return; - } - - const sizes = { - default: { - height: 40, - minHeight: 40, - paddingTop: 0, - paddingBottom: 0, - }, - small: { - height: 24, - minHeight: 24, - paddingTop: 0, - paddingBottom: 0, - }, - compact: { - height: 32, - minHeight: 32, - paddingTop: 0, - paddingBottom: 0, - }, - }; - - const style = sizes[ selectSize ] || sizes.default; - - return css( style ); -}; - -export const chevronIconSize = 18; - -const sizePaddings = ( { multiple, selectSize = 'default' }: SelectProps ) => { - const padding = { - default: CONFIG.controlPaddingX, - small: CONFIG.controlPaddingXSmall, - compact: CONFIG.controlPaddingXSmall, - }; - - const selectedPadding = padding[ selectSize ] || padding.default; - - return rtl( { - paddingLeft: selectedPadding, - paddingRight: selectedPadding + chevronIconSize, - ...( multiple - ? { - paddingTop: selectedPadding, - paddingBottom: selectedPadding, - } - : {} ), - } ); -}; - -const overflowStyles = ( { multiple }: SelectProps ) => { - return { - overflow: multiple ? 'auto' : 'hidden', - }; -}; - -const variantStyles = ( { variant }: SelectProps ) => { - if ( variant === 'minimal' ) { - return css( { - fieldSizing: 'content', - } ); - } - - return ''; -}; - -// TODO: Resolve need to use &&& to increase specificity -// https://github.com/WordPress/gutenberg/issues/18483 - -export const Select = styled.select< SelectProps >` - &&& { - appearance: none; - background: transparent; - box-sizing: border-box; - border: none; - box-shadow: none !important; - color: currentColor; // Overrides hover/focus styles in forms.css - cursor: inherit; - display: block; - font-family: inherit; - line-height: 1.3; // Override forms.css styles, large enough value to prevent descender clipping without affecting height - margin: 0; - width: 100%; - max-width: none; - white-space: nowrap; - text-overflow: ellipsis; - - ${ fontSizeStyles }; - ${ sizeStyles }; - ${ sizePaddings }; - ${ overflowStyles } - ${ variantStyles } - } -`; - -export const DownArrowWrapper = styled.div` - margin-inline-end: ${ space( -1 ) }; // optically adjust the icon - line-height: 0; -`; - -export const InputControlSuffixWrapperWithClickThrough = styled( - InputControlSuffixWrapper -)` - position: absolute; - pointer-events: none; - - ${ rtl( { right: 0 } ) } -`; diff --git a/tools/eslint/suppressions.json b/tools/eslint/suppressions.json index 3c123074110ca3..e8cc34b1752c54 100644 --- a/tools/eslint/suppressions.json +++ b/tools/eslint/suppressions.json @@ -4588,11 +4588,6 @@ "count": 4 } }, - "packages/components/src/select-control/styles/select-control-styles.ts": { - "no-restricted-imports": { - "count": 2 - } - }, "packages/components/src/slot-fill/slot.tsx": { "react-hooks/refs": { "count": 1