diff --git a/change/@fluentui-react-infobutton-674485ad-8621-45a2-92ab-bc4d3a6b9004.json b/change/@fluentui-react-infobutton-674485ad-8621-45a2-92ab-bc4d3a6b9004.json new file mode 100644 index 00000000000000..f63fe406c3b872 --- /dev/null +++ b/change/@fluentui-react-infobutton-674485ad-8621-45a2-92ab-bc4d3a6b9004.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "chore: Create InfoTip component.", + "packageName": "@fluentui/react-infobutton", + "email": "esteban.230@hotmail.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-infobutton/etc/react-infobutton.api.md b/packages/react-components/react-infobutton/etc/react-infobutton.api.md index 46b9efca38289a..3ac6dd190275c9 100644 --- a/packages/react-components/react-infobutton/etc/react-infobutton.api.md +++ b/packages/react-components/react-infobutton/etc/react-infobutton.api.md @@ -58,12 +58,32 @@ export type InfoLabelSlots = { // @public export type InfoLabelState = ComponentState & Pick; +// @public +export const InfoTip: ForwardRefComponent; + +// @public (undocumented) +export const infoTipClassNames: SlotClassNames; + +// @public +export type InfoTipProps = ComponentProps & {}; + +// @public (undocumented) +export type InfoTipSlots = { + root: Slot<'div'>; +}; + +// @public +export type InfoTipState = ComponentState; + // @public export const renderInfoButton_unstable: (state: InfoButtonState) => JSX.Element; // @public export const renderInfoLabel_unstable: (state: InfoLabelState) => JSX.Element; +// @public +export const renderInfoTip_unstable: (state: InfoTipState) => JSX.Element; + // @public export const useInfoButton_unstable: (props: InfoButtonProps, ref: React_2.Ref) => InfoButtonState; @@ -76,6 +96,12 @@ export const useInfoLabel_unstable: (props: InfoLabelProps, ref: React_2.Ref InfoLabelState; +// @public +export const useInfoTip_unstable: (props: InfoTipProps, ref: React_2.Ref) => InfoTipState; + +// @public +export const useInfoTipStyles_unstable: (state: InfoTipState) => InfoTipState; + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/react-components/react-infobutton/src/InfoTip.ts b/packages/react-components/react-infobutton/src/InfoTip.ts new file mode 100644 index 00000000000000..a0c6c5c087ed93 --- /dev/null +++ b/packages/react-components/react-infobutton/src/InfoTip.ts @@ -0,0 +1 @@ +export * from './components/InfoTip/index'; diff --git a/packages/react-components/react-infobutton/src/components/InfoTip/InfoTip.test.tsx b/packages/react-components/react-infobutton/src/components/InfoTip/InfoTip.test.tsx new file mode 100644 index 00000000000000..4fe15be70f7adf --- /dev/null +++ b/packages/react-components/react-infobutton/src/components/InfoTip/InfoTip.test.tsx @@ -0,0 +1,18 @@ +import * as React from 'react'; +import { render } from '@testing-library/react'; +import { InfoTip } from './InfoTip'; +import { isConformant } from '../../testing/isConformant'; + +describe('InfoTip', () => { + isConformant({ + Component: InfoTip, + displayName: 'InfoTip', + }); + + // TODO add more tests here, and create visual regression tests in /apps/vr-tests + + it('renders a default state', () => { + const result = render(Default InfoTip); + expect(result.container).toMatchSnapshot(); + }); +}); diff --git a/packages/react-components/react-infobutton/src/components/InfoTip/InfoTip.tsx b/packages/react-components/react-infobutton/src/components/InfoTip/InfoTip.tsx new file mode 100644 index 00000000000000..9340f4ee8e8aad --- /dev/null +++ b/packages/react-components/react-infobutton/src/components/InfoTip/InfoTip.tsx @@ -0,0 +1,18 @@ +import * as React from 'react'; +import { useInfoTip_unstable } from './useInfoTip'; +import { renderInfoTip_unstable } from './renderInfoTip'; +import { useInfoTipStyles_unstable } from './useInfoTipStyles.styles'; +import type { InfoTipProps } from './InfoTip.types'; +import type { ForwardRefComponent } from '@fluentui/react-utilities'; + +/** + * InfoTip component - TODO: add more docs + */ +export const InfoTip: ForwardRefComponent = React.forwardRef((props, ref) => { + const state = useInfoTip_unstable(props, ref); + + useInfoTipStyles_unstable(state); + return renderInfoTip_unstable(state); +}); + +InfoTip.displayName = 'InfoTip'; diff --git a/packages/react-components/react-infobutton/src/components/InfoTip/InfoTip.types.ts b/packages/react-components/react-infobutton/src/components/InfoTip/InfoTip.types.ts new file mode 100644 index 00000000000000..f7a24c66420a6b --- /dev/null +++ b/packages/react-components/react-infobutton/src/components/InfoTip/InfoTip.types.ts @@ -0,0 +1,17 @@ +import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; + +export type InfoTipSlots = { + root: Slot<'div'>; +}; + +/** + * InfoTip Props + */ +export type InfoTipProps = ComponentProps & {}; + +/** + * State used in rendering InfoTip + */ +export type InfoTipState = ComponentState; +// TODO: Remove semicolon from previous line, uncomment next line, and provide union of props to pick from InfoTipProps. +// & Required> diff --git a/packages/react-components/react-infobutton/src/components/InfoTip/__snapshots__/InfoTip.test.tsx.snap b/packages/react-components/react-infobutton/src/components/InfoTip/__snapshots__/InfoTip.test.tsx.snap new file mode 100644 index 00000000000000..78459d67a5dab2 --- /dev/null +++ b/packages/react-components/react-infobutton/src/components/InfoTip/__snapshots__/InfoTip.test.tsx.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`InfoTip renders a default state 1`] = ` +
+
+ Default InfoTip +
+
+`; diff --git a/packages/react-components/react-infobutton/src/components/InfoTip/index.ts b/packages/react-components/react-infobutton/src/components/InfoTip/index.ts new file mode 100644 index 00000000000000..d2049a239faf4a --- /dev/null +++ b/packages/react-components/react-infobutton/src/components/InfoTip/index.ts @@ -0,0 +1,5 @@ +export * from './InfoTip'; +export * from './InfoTip.types'; +export * from './renderInfoTip'; +export * from './useInfoTip'; +export * from './useInfoTipStyles.styles'; diff --git a/packages/react-components/react-infobutton/src/components/InfoTip/renderInfoTip.tsx b/packages/react-components/react-infobutton/src/components/InfoTip/renderInfoTip.tsx new file mode 100644 index 00000000000000..68a86967064c35 --- /dev/null +++ b/packages/react-components/react-infobutton/src/components/InfoTip/renderInfoTip.tsx @@ -0,0 +1,16 @@ +/** @jsxRuntime classic */ +/** @jsx createElement */ + +import { createElement } from '@fluentui/react-jsx-runtime'; +import { getSlotsNext } from '@fluentui/react-utilities'; +import type { InfoTipState, InfoTipSlots } from './InfoTip.types'; + +/** + * Render the final JSX of InfoTip + */ +export const renderInfoTip_unstable = (state: InfoTipState) => { + const { slots, slotProps } = getSlotsNext(state); + + // TODO Add additional slots in the appropriate place + return ; +}; diff --git a/packages/react-components/react-infobutton/src/components/InfoTip/useInfoTip.ts b/packages/react-components/react-infobutton/src/components/InfoTip/useInfoTip.ts new file mode 100644 index 00000000000000..f75a158772025f --- /dev/null +++ b/packages/react-components/react-infobutton/src/components/InfoTip/useInfoTip.ts @@ -0,0 +1,28 @@ +import * as React from 'react'; +import { getNativeElementProps } from '@fluentui/react-utilities'; +import type { InfoTipProps, InfoTipState } from './InfoTip.types'; + +/** + * Create the state required to render InfoTip. + * + * The returned state can be modified with hooks such as useInfoTipStyles_unstable, + * before being passed to renderInfoTip_unstable. + * + * @param props - props from this instance of InfoTip + * @param ref - reference to root HTMLElement of InfoTip + */ +export const useInfoTip_unstable = (props: InfoTipProps, ref: React.Ref): InfoTipState => { + return { + // TODO add appropriate props/defaults + components: { + // TODO add each slot's element type or component + root: 'div', + }, + // TODO add appropriate slots, for example: + // mySlot: resolveShorthand(props.mySlot), + root: getNativeElementProps('div', { + ref, + ...props, + }), + }; +}; diff --git a/packages/react-components/react-infobutton/src/components/InfoTip/useInfoTipStyles.styles.ts b/packages/react-components/react-infobutton/src/components/InfoTip/useInfoTipStyles.styles.ts new file mode 100644 index 00000000000000..a8583f8b5596cc --- /dev/null +++ b/packages/react-components/react-infobutton/src/components/InfoTip/useInfoTipStyles.styles.ts @@ -0,0 +1,33 @@ +import { makeStyles, mergeClasses } from '@griffel/react'; +import type { InfoTipSlots, InfoTipState } from './InfoTip.types'; +import type { SlotClassNames } from '@fluentui/react-utilities'; + +export const infoTipClassNames: SlotClassNames = { + root: 'fui-InfoTip', + // TODO: add class names for all slots on InfoTipSlots. + // Should be of the form `: 'fui-InfoTip__` +}; + +/** + * Styles for the root slot + */ +const useStyles = makeStyles({ + root: { + // TODO Add default styles for the root element + }, + + // TODO add additional classes for different states and/or slots +}); + +/** + * Apply styling to the InfoTip slots based on the state + */ +export const useInfoTipStyles_unstable = (state: InfoTipState): InfoTipState => { + const styles = useStyles(); + state.root.className = mergeClasses(infoTipClassNames.root, styles.root, state.root.className); + + // TODO Add class names to slots, for example: + // state.mySlot.className = mergeClasses(styles.mySlot, state.mySlot.className); + + return state; +}; diff --git a/packages/react-components/react-infobutton/src/index.ts b/packages/react-components/react-infobutton/src/index.ts index 2f13cdd25b29d4..9315e64b48b42e 100644 --- a/packages/react-components/react-infobutton/src/index.ts +++ b/packages/react-components/react-infobutton/src/index.ts @@ -14,3 +14,11 @@ export { useInfoLabel_unstable, } from './InfoLabel'; export type { InfoLabelProps, InfoLabelSlots, InfoLabelState } from './InfoLabel'; +export { + InfoTip, + infoTipClassNames, + renderInfoTip_unstable, + useInfoTipStyles_unstable, + useInfoTip_unstable, +} from './InfoTip'; +export type { InfoTipProps, InfoTipSlots, InfoTipState } from './InfoTip'; diff --git a/packages/react-components/react-infobutton/stories/InfoTip/InfoTipBestPractices.md b/packages/react-components/react-infobutton/stories/InfoTip/InfoTipBestPractices.md new file mode 100644 index 00000000000000..08ff8ddeeb5f86 --- /dev/null +++ b/packages/react-components/react-infobutton/stories/InfoTip/InfoTipBestPractices.md @@ -0,0 +1,5 @@ +## Best practices + +### Do + +### Don't diff --git a/packages/react-components/react-infobutton/stories/InfoTip/InfoTipDefault.stories.tsx b/packages/react-components/react-infobutton/stories/InfoTip/InfoTipDefault.stories.tsx new file mode 100644 index 00000000000000..6baa633d381b52 --- /dev/null +++ b/packages/react-components/react-infobutton/stories/InfoTip/InfoTipDefault.stories.tsx @@ -0,0 +1,4 @@ +import * as React from 'react'; +import { InfoTip, InfoTipProps } from '@fluentui/react-infobutton'; + +export const Default = (props: Partial) => ; diff --git a/packages/react-components/react-infobutton/stories/InfoTip/InfoTipDescription.md b/packages/react-components/react-infobutton/stories/InfoTip/InfoTipDescription.md new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/packages/react-components/react-infobutton/stories/InfoTip/index.stories.tsx b/packages/react-components/react-infobutton/stories/InfoTip/index.stories.tsx new file mode 100644 index 00000000000000..076ebbdd14603b --- /dev/null +++ b/packages/react-components/react-infobutton/stories/InfoTip/index.stories.tsx @@ -0,0 +1,18 @@ +import { InfoTip } from '@fluentui/react-infobutton'; + +import descriptionMd from './InfoTipDescription.md'; +import bestPracticesMd from './InfoTipBestPractices.md'; + +export { Default } from './InfoTipDefault.stories'; + +export default { + title: 'Preview Components/InfoTip', + component: InfoTip, + parameters: { + docs: { + description: { + component: [descriptionMd, bestPracticesMd].join('\n'), + }, + }, + }, +};