Skip to content

Commit

Permalink
feat(floating-ui): add feature flag for dynamic floating-ui styles (#…
Browse files Browse the repository at this point in the history
…17727)

* feat(feature-flag): create floating styles feature flag

* feat(dropdown): add feature flag logic to dropdown and popover

* feat(toggletip): add feature flag story

* feat(floating-ui): add additional components

* chore: shorten flag name

* chore: shorten flag name

* chore: the rest of the auto align components

* chore: set a few controls

* Update packages/react/src/components/FeatureFlags/overview.mdx

Co-authored-by: kennylam <[email protected]>

* chore(storybook): add meta isTemplate to overview doc

* Update packages/react/src/components/Popover/DynamicStyles.featureflag.mdx

Co-authored-by: Taylor Jones <[email protected]>

* fix: ensure popover fix is included

---------

Co-authored-by: kennylam <[email protected]>
Co-authored-by: Taylor Jones <[email protected]>
  • Loading branch information
3 people authored Oct 11, 2024
1 parent 107e572 commit 4b4fe02
Show file tree
Hide file tree
Showing 11 changed files with 582 additions and 64 deletions.
5 changes: 5 additions & 0 deletions packages/feature-flags/feature-flags.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,8 @@ feature-flags:
description: >
Enable the new focus wrap behavior that doesn't use sentinel nodes
enabled: false
- name: enable-v12-dynamic-floating-styles
description: >
Enable dynamic setting of floating styles for components like Popover,
Tooltip, etc.
enabled: false
21 changes: 13 additions & 8 deletions packages/react/src/components/ComboBox/ComboBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import { FormContext } from '../FluidForm';
import { useFloating, flip, autoUpdate } from '@floating-ui/react';
import { hide } from '@floating-ui/dom';
import { TranslateWithId } from '../../types/common';
import { useFeatureFlag } from '../FeatureFlags';

const {
InputBlur,
Expand Down Expand Up @@ -417,20 +418,24 @@ const ComboBox = forwardRef(
slug,
...rest
} = props;

const enableFloatingStyles =
useFeatureFlag('enable-v12-dynamic-floating-styles') || autoAlign;

const { refs, floatingStyles, middlewareData } = useFloating(
autoAlign
enableFloatingStyles
? {
placement: direction,
strategy: 'fixed',
middleware: [flip(), hide()],
middleware: autoAlign ? [flip(), hide()] : undefined,
whileElementsMounted: autoUpdate,
}
: {}
);
const parentWidth = (refs?.reference?.current as HTMLElement)?.clientWidth;

useEffect(() => {
if (autoAlign) {
if (enableFloatingStyles) {
const updatedFloatingStyles = {
...floatingStyles,
visibility: middlewareData.hide?.referenceHidden
Expand All @@ -446,7 +451,7 @@ const ComboBox = forwardRef(
refs.floating.current.style.width = parentWidth + 'px';
}
}
}, [autoAlign, floatingStyles, refs.floating, parentWidth]);
}, [enableFloatingStyles, floatingStyles, refs.floating, parentWidth]);

const [inputValue, setInputValue] = useState(
getInputValue({
Expand Down Expand Up @@ -668,7 +673,7 @@ const ComboBox = forwardRef(
[`${prefix}--list-box--up`]: direction === 'top',
[`${prefix}--combo-box--warning`]: showWarning,
[`${prefix}--combo-box--readonly`]: readOnly,
[`${prefix}--autoalign`]: autoAlign,
[`${prefix}--autoalign`]: enableFloatingStyles,
});

const titleClasses = cx(`${prefix}--label`, {
Expand Down Expand Up @@ -851,10 +856,10 @@ const ComboBox = forwardRef(
const menuProps = useMemo(
() =>
getMenuProps({
ref: autoAlign ? refs.setFloating : null,
ref: enableFloatingStyles ? refs.setFloating : null,
}),
[
autoAlign,
enableFloatingStyles,
deprecatedAriaLabel,
ariaLabel,
getMenuProps,
Expand Down Expand Up @@ -892,7 +897,7 @@ const ComboBox = forwardRef(
light={light}
size={size}
warn={warn}
ref={autoAlign ? refs.setReference : null}
ref={enableFloatingStyles ? refs.setReference : null}
warnText={warnText}
warnTextId={warnTextId}>
<div className={`${prefix}--list-box__field`}>
Expand Down
13 changes: 12 additions & 1 deletion packages/react/src/components/ComboButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
autoUpdate,
} from '@floating-ui/react';
import { hide } from '@floating-ui/dom';
import { useFeatureFlag } from '../FeatureFlags';
import mergeRefs from '../../tools/mergeRefs';
import { MenuAlignment } from '../MenuButton';
import { TranslateWithId } from '../../types/common';
Expand Down Expand Up @@ -97,10 +98,20 @@ const ComboButton = React.forwardRef<HTMLDivElement, ComboButtonProps>(
},
forwardRef
) {
// feature flag utilized to separate out only the dynamic styles from @floating-ui
// flag is turned on when collision detection (ie. flip, hide) logic is not desired
const enableOnlyFloatingStyles = useFeatureFlag(
'enable-v12-dynamic-floating-styles'
);

const id = useId('combobutton');
const prefix = usePrefix();
const containerRef = useRef<HTMLDivElement>(null);
const middlewares = [flip({ crossAxis: false }), hide()];
let middlewares: any[] = [];

if (!enableOnlyFloatingStyles) {
middlewares = [flip({ crossAxis: false }), hide()];
}

if (menuAlignment === 'bottom' || menuAlignment === 'top') {
middlewares.push(
Expand Down
21 changes: 14 additions & 7 deletions packages/react/src/components/Dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import {
size as floatingSize,
} from '@floating-ui/react';
import { hide } from '@floating-ui/dom';
import { useFeatureFlag } from '../FeatureFlags';

const { ItemMouseMove, MenuMouseLeave } =
useSelect.stateChangeTypes as UseSelectInterface['stateChangeTypes'] & {
Expand Down Expand Up @@ -274,8 +275,12 @@ const Dropdown = React.forwardRef(
}: DropdownProps<ItemType>,
ref: ForwardedRef<HTMLButtonElement>
) => {
const enableFloatingStyles = useFeatureFlag(
'enable-v12-dynamic-floating-styles'
);

const { refs, floatingStyles, middlewareData } = useFloating(
autoAlign
enableFloatingStyles || autoAlign
? {
placement: direction,

Expand All @@ -294,16 +299,18 @@ const Dropdown = React.forwardRef(
});
},
}),
flip(),
hide(),
autoAlign && flip(),
autoAlign && hide(),
],
whileElementsMounted: autoUpdate,
}
: {} // When autoAlign is turned off, floating-ui will not be used
: {}
// When autoAlign is turned off & the `enable-v12-dynamic-floating-styles` feature flag is not
// enabled, floating-ui will not be used
);

useEffect(() => {
if (autoAlign) {
if (enableFloatingStyles || autoAlign) {
const updatedFloatingStyles = {
...floatingStyles,
visibility: middlewareData.hide?.referenceHidden
Expand Down Expand Up @@ -503,7 +510,7 @@ const Dropdown = React.forwardRef(
const menuProps = useMemo(
() =>
getMenuProps({
ref: autoAlign ? refs.setFloating : null,
ref: enableFloatingStyles || autoAlign ? refs.setFloating : null,
}),
[autoAlign, getMenuProps, refs.setFloating]
);
Expand Down Expand Up @@ -534,7 +541,7 @@ const Dropdown = React.forwardRef(
warnText={warnText}
light={light}
isOpen={isOpen}
ref={autoAlign ? refs.setReference : null}
ref={enableFloatingStyles || autoAlign ? refs.setReference : null}
id={id}>
{invalid && (
<WarningFilled className={`${prefix}--list-box__invalid-icon`} />
Expand Down
19 changes: 10 additions & 9 deletions packages/react/src/components/FeatureFlags/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,16 @@ For more details on this approach, see the
[feature flag documentation](https://github.com/carbon-design-system/carbon/blob/main/docs/experimental-code.md)
in the Carbon monorepo.

| Flag | Description | Default | Javascript flag | Sass flag |
| -------------------------------------------------- | ------------------------------------------------------------------------ | ------- | --------------- | --------- |
| `enable-experimental-tile-contrast` | Enable the improved styling for tiles that provides better contrast | `false` | ||
| `enable-experimental-focus-wrap-without-sentinels` | Enable the new focus wrap behavior that doesn't use sentinel nodes | `false` || |
| `enable-treeview-controllable` | Enable the new TreeView controllable API | `false` || |
| `enable-v12-tile-default-icons` | Enable default icons for Tile components | `false` || |
| `enable-v12-overflowmenu` | Enable the use of the v12 OverflowMenu leveraging the Menu subcomponents | `false` || |
| `enable-v12-tile-radio-icons` | Enable rendering of default icons in the tile components | `false` |||
| `enable-v12-structured-list-visible-icons` | Enable icon components within StructuredList to always be visible | `false` | ||
| Flag | Description | Default | Javascript flag | Sass flag |
| -------------------------------------------------- | ------------------------------------------------------------------------------------ | ------- | --------------- | --------- |
| `enable-experimental-tile-contrast` | Enable the improved styling for tiles that provides better contrast | `false` | ||
| `enable-experimental-focus-wrap-without-sentinels` | Enable the new focus wrap behavior that doesn't use sentinel nodes | `false` || |
| `enable-treeview-controllable` | Enable the new TreeView controllable API | `false` || |
| `enable-v12-tile-default-icons` | Enable default icons for Tile components | `false` || |
| `enable-v12-overflowmenu` | Enable the use of the v12 OverflowMenu leveraging the Menu subcomponents | `false` || |
| `enable-v12-tile-radio-icons` | Enable rendering of default icons in the tile components | `false` |||
| `enable-v12-structured-list-visible-icons` | Enable icon components within StructuredList to always be visible | `false` | ||
| `enable-v12-dynamic-floating-styles` | Enable dynamic setting of floating styles for components like Popover, Tooltip, etc. | `false` |||

## Turning on feature flags in Javascript/react

Expand Down
14 changes: 13 additions & 1 deletion packages/react/src/components/MenuButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
size as floatingSize,
autoUpdate,
} from '@floating-ui/react';
import { useFeatureFlag } from '../FeatureFlags';
import mergeRefs from '../../tools/mergeRefs';

const validButtonKinds = ['primary', 'tertiary', 'ghost'];
Expand Down Expand Up @@ -97,10 +98,20 @@ const MenuButton = forwardRef<HTMLDivElement, MenuButtonProps>(
},
forwardRef
) {
// feature flag utilized to separate out only the dynamic styles from @floating-ui
// flag is turned on when collision detection (ie. flip, hide) logic is not desired
const enableOnlyFloatingStyles = useFeatureFlag(
'enable-v12-dynamic-floating-styles'
);

const id = useId('MenuButton');
const prefix = usePrefix();
const triggerRef = useRef<HTMLDivElement>(null);
const middlewares = [flip({ crossAxis: false })];
let middlewares: any[] = [];

if (!enableOnlyFloatingStyles) {
middlewares = [flip({ crossAxis: false })];
}

if (menuAlignment === 'bottom' || menuAlignment === 'top') {
middlewares.push(
Expand All @@ -113,6 +124,7 @@ const MenuButton = forwardRef<HTMLDivElement, MenuButtonProps>(
})
);
}

const { refs, floatingStyles, placement, middlewareData } = useFloating({
placement: menuAlignment,

Expand Down
28 changes: 19 additions & 9 deletions packages/react/src/components/MultiSelect/MultiSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import {
autoUpdate,
} from '@floating-ui/react';
import { hide } from '@floating-ui/dom';
import { useFeatureFlag } from '../FeatureFlags';

const {
ItemClick,
Expand Down Expand Up @@ -362,8 +363,11 @@ const MultiSelect = React.forwardRef(
const [topItems, setTopItems] = useState([]);
const [itemsCleared, setItemsCleared] = useState(false);

const enableFloatingStyles =
useFeatureFlag('enable-v12-dynamic-floating-styles') || autoAlign;

const { refs, floatingStyles, middlewareData } = useFloating(
autoAlign
enableFloatingStyles
? {
placement: direction,

Expand All @@ -375,23 +379,23 @@ const MultiSelect = React.forwardRef(

// Middleware order matters, arrow should be last
middleware: [
flip({ crossAxis: false }),
autoAlign && flip({ crossAxis: false }),
floatingSize({
apply({ rects, elements }) {
Object.assign(elements.floating.style, {
width: `${rects.reference.width}px`,
});
},
}),
hide(),
autoAlign && hide(),
],
whileElementsMounted: autoUpdate,
}
: {}
);

useLayoutEffect(() => {
if (autoAlign) {
if (enableFloatingStyles) {
const updatedFloatingStyles = {
...floatingStyles,
visibility: middlewareData.hide?.referenceHidden
Expand All @@ -404,7 +408,13 @@ const MultiSelect = React.forwardRef(
}
});
}
}, [autoAlign, floatingStyles, refs.floating, middlewareData, open]);
}, [
enableFloatingStyles,
floatingStyles,
refs.floating,
middlewareData,
open,
]);

const {
selectedItems: controlledSelectedItems,
Expand Down Expand Up @@ -551,7 +561,7 @@ const MultiSelect = React.forwardRef(
selectedItems && selectedItems.length > 0,
[`${prefix}--list-box--up`]: direction === 'top',
[`${prefix}--multi-select--readonly`]: readOnly,
[`${prefix}--autoalign`]: autoAlign,
[`${prefix}--autoalign`]: enableFloatingStyles,
[`${prefix}--multi-select--selectall`]: selectAll,
});

Expand Down Expand Up @@ -689,9 +699,9 @@ const MultiSelect = React.forwardRef(
const menuProps = useMemo(
() =>
getMenuProps({
ref: autoAlign ? refs.setFloating : null,
ref: enableFloatingStyles ? refs.setFloating : null,
}),
[autoAlign, getMenuProps, refs.setFloating]
[enableFloatingStyles, getMenuProps, refs.setFloating]
);

return (
Expand Down Expand Up @@ -729,7 +739,7 @@ const MultiSelect = React.forwardRef(
)}
<div
className={multiSelectFieldWrapperClasses}
ref={autoAlign ? refs.setReference : null}>
ref={enableFloatingStyles ? refs.setReference : null}>
{selectedItems.length > 0 && (
<ListBox.Selection
readOnly={readOnly}
Expand Down
Loading

0 comments on commit 4b4fe02

Please sign in to comment.