= Partial & {
+export type ComponentProps = Partial & {
readonly value: T;
};
+export type MaybeComponentProps = Partial<
+ Omit
+> & {
+ readonly value: undefined | null | T;
+};
+
export type MaybeRendererProps = Partial<
Omit
> & {
@@ -26,3 +32,29 @@ export const MaybeRenderer = ({
// `rest` has already had everything that's not ComponentProps removed.
return )} value={value} />;
};
+
+/**
+ * Binds a regular renderer into a MaybeRenderer so it can be used via e.g. the
+ * render prop of a
+ * @param Component
+ */
+export function maybeRender(
+ Component: React.ComponentType>
+) {
+ /** Wrapped version of Component which allows for undefined/null values */
+ function wrapped(props: MaybeComponentProps) {
+ return ;
+ }
+ wrapped.displayName = `Maybe${Component.displayName ?? Component.name}`;
+ return wrapped;
+}
+
+/**
+ * Returns a memoized version of Component bound to a MaybeRenderer
+ * @param Component
+ */
+export function useMaybeRender(
+ Component: React.ComponentType>
+) {
+ return useMemo(() => maybeRender(Component), [Component]);
+}
From 698bce45208d0ab1f63d81d88ea292c1920dc77c Mon Sep 17 00:00:00 2001
From: ianwremmel <1182361+ianwremmel@users.noreply.github.com>
Date: Tue, 30 May 2023 08:54:44 -0700
Subject: [PATCH 2/4] feat(data-display): add DataGrid
[Finishes #185284007]
---
src/data-display/data-grid/csstype.d.ts | 12 +++
src/data-display/data-grid/data-grid-item.tsx | 33 +++++++
.../data-grid/data-grid.stories.tsx | 97 +++++++++++++++++++
src/data-display/data-grid/data-grid.tsx | 72 ++++++++++++++
src/data-display/data-grid/index.tsx | 2 +
5 files changed, 216 insertions(+)
create mode 100644 src/data-display/data-grid/csstype.d.ts
create mode 100644 src/data-display/data-grid/data-grid-item.tsx
create mode 100644 src/data-display/data-grid/data-grid.stories.tsx
create mode 100644 src/data-display/data-grid/data-grid.tsx
create mode 100644 src/data-display/data-grid/index.tsx
diff --git a/src/data-display/data-grid/csstype.d.ts b/src/data-display/data-grid/csstype.d.ts
new file mode 100644
index 00000000..0100fcd1
--- /dev/null
+++ b/src/data-display/data-grid/csstype.d.ts
@@ -0,0 +1,12 @@
+// eslint-disable-next-line import/no-unresolved
+import 'csstype';
+
+declare module 'csstype' {
+ // eslint-disable-next-line @typescript-eslint/ban-types
+ interface Properties {
+ '--data-unit'?: string;
+ '--data-gap'?: string;
+ '--data-grid__item_height'?: string;
+ '--data-grid__item_width'?: string;
+ }
+}
diff --git a/src/data-display/data-grid/data-grid-item.tsx b/src/data-display/data-grid/data-grid-item.tsx
new file mode 100644
index 00000000..4abc5206
--- /dev/null
+++ b/src/data-display/data-grid/data-grid-item.tsx
@@ -0,0 +1,33 @@
+import cx from 'classnames';
+import React, {ReactNode} from 'react';
+
+export interface DataGridItemProps
+ extends React.DetailedHTMLProps<
+ React.HTMLAttributes,
+ HTMLSpanElement
+ > {
+ children: ReactNode;
+ height?: number;
+ width?: number;
+}
+
+export const DataGridItem = ({
+ children,
+ className,
+ height = 1,
+ style,
+ width = 1,
+ ...rest
+}: DataGridItemProps) => (
+
+ {children}
+
+);
diff --git a/src/data-display/data-grid/data-grid.stories.tsx b/src/data-display/data-grid/data-grid.stories.tsx
new file mode 100644
index 00000000..7d892e52
--- /dev/null
+++ b/src/data-display/data-grid/data-grid.stories.tsx
@@ -0,0 +1,97 @@
+import {faker} from '@faker-js/faker';
+import React from 'react';
+
+import {DataGrid} from './data-grid';
+import {DataGridItem} from './data-grid-item';
+
+export default {
+ component: DataGrid,
+ title: 'Data Display/Data Grid',
+};
+
+const sizes = [1, 2, 3];
+
+/** helper */
+function rand() {
+ return faker.helpers.arrayElement(sizes);
+}
+
+export const NormalGrid = () => (
+
+ {new Array(37).fill('x').map((_, i) => (
+
+ {i}
+
+ ))}
+
+);
+
+export const DenseColumns = () => (
+
+ {new Array(37).fill('x').map((_, i) => (
+
+ {i}
+
+ ))}
+
+);
+
+export const DenseRows = () => (
+
+ {new Array(37).fill('x').map((_, i) => (
+
+ {i}
+
+ ))}
+
+);
+
+export const DenseColumnsAndRows = () => (
+
+ {new Array(37).fill('x').map((_, i) => (
+
+ {i}
+
+ ))}
+
+);
diff --git a/src/data-display/data-grid/data-grid.tsx b/src/data-display/data-grid/data-grid.tsx
new file mode 100644
index 00000000..abe64d46
--- /dev/null
+++ b/src/data-display/data-grid/data-grid.tsx
@@ -0,0 +1,72 @@
+import cx from 'classnames';
+import React, {ReactNode} from 'react';
+
+export interface DataGridProps
+ extends React.DetailedHTMLProps<
+ React.HTMLAttributes,
+ HTMLDivElement
+ > {
+ denseColumns?: boolean;
+ denseRows?: boolean;
+ children: ReactNode;
+}
+
+export const DataGrid = ({
+ children,
+ className,
+ denseColumns = false,
+ denseRows = false,
+ ...rest
+}: DataGridProps) => (
+ <>
+ {/* This is... not ideal, but so far, it's the least-worst way I've found to
+ include the data-grid styles in the exported bundle. */}
+
+
+ {children}
+
+ >
+);
diff --git a/src/data-display/data-grid/index.tsx b/src/data-display/data-grid/index.tsx
new file mode 100644
index 00000000..2c277f5a
--- /dev/null
+++ b/src/data-display/data-grid/index.tsx
@@ -0,0 +1,2 @@
+export * from './data-grid-item';
+export * from './data-grid';
From b2bfe17535777dc61c11c64ea6ec4b8e3a5e1aa5 Mon Sep 17 00:00:00 2001
From: ianwremmel <1182361+ianwremmel@users.noreply.github.com>
Date: Tue, 30 May 2023 22:55:33 -0700
Subject: [PATCH 3/4] feat: add Bytes, Currency, Number, and Percent renderers
Also adds useLocale hook
[Finishes #185296293]
[Finishes #185289105]
[Finishes #185289103]
---
src/core/locale.tsx | 16 +++++
src/renderers/any-renderer/any-renderer.tsx | 7 ++-
.../byte-renderer/byte-renderer.stories.tsx | 16 +++++
src/renderers/byte-renderer/byte-renderer.tsx | 58 +++++++++++++++++++
src/renderers/byte-renderer/index.tsx | 1 +
.../currency-renderer.stories.tsx | 12 ++++
.../currency-renderer/currency-renderer.tsx | 42 ++++++++++++++
src/renderers/currency-renderer/index.tsx | 1 +
src/renderers/number-renderer/index.tsx | 1 +
.../number-renderer.stories.tsx | 13 +++++
.../number-renderer/number-renderer.tsx | 41 +++++++++++++
src/renderers/percent-renderer/index.tsx | 1 +
.../percent-renderer.stories.tsx | 19 ++++++
.../percent-renderer/percent-renderer.tsx | 46 +++++++++++++++
14 files changed, 273 insertions(+), 1 deletion(-)
create mode 100644 src/core/locale.tsx
create mode 100644 src/renderers/byte-renderer/byte-renderer.stories.tsx
create mode 100644 src/renderers/byte-renderer/byte-renderer.tsx
create mode 100644 src/renderers/byte-renderer/index.tsx
create mode 100644 src/renderers/currency-renderer/currency-renderer.stories.tsx
create mode 100644 src/renderers/currency-renderer/currency-renderer.tsx
create mode 100644 src/renderers/currency-renderer/index.tsx
create mode 100644 src/renderers/number-renderer/index.tsx
create mode 100644 src/renderers/number-renderer/number-renderer.stories.tsx
create mode 100644 src/renderers/number-renderer/number-renderer.tsx
create mode 100644 src/renderers/percent-renderer/index.tsx
create mode 100644 src/renderers/percent-renderer/percent-renderer.stories.tsx
create mode 100644 src/renderers/percent-renderer/percent-renderer.tsx
diff --git a/src/core/locale.tsx b/src/core/locale.tsx
new file mode 100644
index 00000000..f0630170
--- /dev/null
+++ b/src/core/locale.tsx
@@ -0,0 +1,16 @@
+import {createContext, ReactNode, useContext} from 'react';
+
+// default value is provided for storybook. Otherwise, this should always be set
+// explicitly in the root component.
+export const LocaleContext = createContext(new Intl.Locale('en-US'));
+
+export interface LocaleProviderProps {
+ children: ReactNode;
+ locale: Intl.Locale;
+}
+
+export const LocaleProvider = ({children, locale}: LocaleProviderProps) => (
+ {children}
+);
+
+export const useLocale = () => useContext(LocaleContext);
diff --git a/src/renderers/any-renderer/any-renderer.tsx b/src/renderers/any-renderer/any-renderer.tsx
index d544efe5..769fb110 100644
--- a/src/renderers/any-renderer/any-renderer.tsx
+++ b/src/renderers/any-renderer/any-renderer.tsx
@@ -5,6 +5,7 @@ import {useContextWithDefaults} from '../../support';
import {BooleanRenderer, BooleanRendererContextType} from '../boolean-renderer';
import {DateRenderer, DateRendererContextProps} from '../date-renderer';
import {NullRenderer, NullRendererContextType} from '../null-renderer';
+import {NumberRenderer} from '../number-renderer';
import {ObjectRenderer} from '../object-renderer';
import {RendererProps} from '../types';
@@ -50,7 +51,11 @@ export const AnyRenderer = ({value, ...rest}: AnyRendererProps) => {
return ;
}
- if (typeof value === 'number' || typeof value === 'bigint') {
+ if (typeof value === 'number') {
+ return ;
+ }
+
+ if (typeof value === 'bigint') {
return <>{value}>;
}
diff --git a/src/renderers/byte-renderer/byte-renderer.stories.tsx b/src/renderers/byte-renderer/byte-renderer.stories.tsx
new file mode 100644
index 00000000..b1e3460e
--- /dev/null
+++ b/src/renderers/byte-renderer/byte-renderer.stories.tsx
@@ -0,0 +1,16 @@
+import {ByteRenderer} from './byte-renderer';
+
+export default {
+ component: ByteRenderer,
+ title: 'Renderers/ByteRenderer',
+};
+
+export const integer = () => ;
+export const float = () => ;
+export const kb = () => ;
+export const mb = () => ;
+export const gb = () => ;
+export const fixed = () => ;
+export const precision = () => ;
+export const nan = () => ;
+export const infinity = () => ;
diff --git a/src/renderers/byte-renderer/byte-renderer.tsx b/src/renderers/byte-renderer/byte-renderer.tsx
new file mode 100644
index 00000000..6afbf4f9
--- /dev/null
+++ b/src/renderers/byte-renderer/byte-renderer.tsx
@@ -0,0 +1,58 @@
+import React, {useMemo} from 'react';
+
+import {useLocale} from '../../core/locale';
+import {useContextWithDefaults} from '../../support';
+import {NumberRendererContextType} from '../number-renderer';
+import {RendererProps} from '../types';
+
+export type UnitRendererContextType = NumberRendererContextType;
+
+export const UnitRendererContext = React.createContext(
+ {}
+);
+
+export type UnitRendererProps = RendererProps;
+
+export const ByteRenderer = ({value, ...rest}: UnitRendererProps) => {
+ const locale = useLocale();
+
+ const {fixed, precision} = useContextWithDefaults(UnitRendererContext, rest);
+
+ const {unit, val} = useMemo(() => {
+ if (Number.isNaN(value) || !Number.isFinite(value)) {
+ return {unit: 'byte', val: value};
+ }
+ const decimals = fixed ?? 2;
+ const k = 1024;
+ const dm = decimals < 0 ? 0 : decimals;
+ const sizes = [
+ 'byte',
+ 'kilobyte',
+ 'megabyte',
+ 'gigabyte',
+ 'terabyte',
+ 'petabyte',
+ 'exabyte',
+ 'zettabyte',
+ 'yottabyte',
+ ];
+
+ const i = Math.floor(Math.log(value) / Math.log(k));
+ return {unit: sizes[i], val: Number((value / Math.pow(k, i)).toFixed(dm))};
+ }, [fixed, value]);
+
+ const nf = useMemo(
+ () =>
+ new Intl.NumberFormat(locale.language, {
+ maximumFractionDigits: fixed,
+ maximumSignificantDigits: precision,
+ minimumFractionDigits: fixed,
+ minimumSignificantDigits: precision,
+ style: 'unit',
+ unit,
+ }),
+ [fixed, locale.language, precision, unit]
+ );
+
+ return {nf.format(val)};
+};
diff --git a/src/renderers/byte-renderer/index.tsx b/src/renderers/byte-renderer/index.tsx
new file mode 100644
index 00000000..9d59b82c
--- /dev/null
+++ b/src/renderers/byte-renderer/index.tsx
@@ -0,0 +1 @@
+export * from './byte-renderer';
diff --git a/src/renderers/currency-renderer/currency-renderer.stories.tsx b/src/renderers/currency-renderer/currency-renderer.stories.tsx
new file mode 100644
index 00000000..b3fdd96e
--- /dev/null
+++ b/src/renderers/currency-renderer/currency-renderer.stories.tsx
@@ -0,0 +1,12 @@
+import {CurrencyRenderer} from './currency-renderer';
+
+export default {
+ component: CurrencyRenderer,
+ title: 'Renderers/CurrencyRenderer',
+};
+
+export const fixed = () => ;
+export const precision = () => ;
+export const nan = () => ;
+export const infinity = () => ;
+export const nonUSD = () => ;
diff --git a/src/renderers/currency-renderer/currency-renderer.tsx b/src/renderers/currency-renderer/currency-renderer.tsx
new file mode 100644
index 00000000..c21243d3
--- /dev/null
+++ b/src/renderers/currency-renderer/currency-renderer.tsx
@@ -0,0 +1,42 @@
+import React, {useMemo} from 'react';
+
+import {useLocale} from '../../core/locale';
+import {useContextWithDefaults} from '../../support';
+import {NumberRendererContextType} from '../number-renderer';
+import {RendererProps} from '../types';
+
+export interface CurrencyRendererContextType extends NumberRendererContextType {
+ currency?: string;
+}
+
+export const CurrencyRendererContext =
+ React.createContext({currency: 'USD'});
+
+export type CurrencyRendererProps = RendererProps<
+ number,
+ CurrencyRendererContextType
+>;
+
+export const CurrencyRenderer = ({value, ...rest}: CurrencyRendererProps) => {
+ const locale = useLocale();
+
+ const {currency, fixed, precision} = useContextWithDefaults(
+ CurrencyRendererContext,
+ rest
+ );
+
+ const nf = useMemo(
+ () =>
+ new Intl.NumberFormat(locale.language, {
+ currency,
+ maximumFractionDigits: fixed,
+ maximumSignificantDigits: precision,
+ minimumFractionDigits: fixed,
+ minimumSignificantDigits: precision,
+ style: 'currency',
+ }),
+ [currency, fixed, locale.language, precision]
+ );
+
+ return {nf.format(value)};
+};
diff --git a/src/renderers/currency-renderer/index.tsx b/src/renderers/currency-renderer/index.tsx
new file mode 100644
index 00000000..604a210e
--- /dev/null
+++ b/src/renderers/currency-renderer/index.tsx
@@ -0,0 +1 @@
+export * from './currency-renderer';
diff --git a/src/renderers/number-renderer/index.tsx b/src/renderers/number-renderer/index.tsx
new file mode 100644
index 00000000..ed0e28d4
--- /dev/null
+++ b/src/renderers/number-renderer/index.tsx
@@ -0,0 +1 @@
+export * from './number-renderer';
diff --git a/src/renderers/number-renderer/number-renderer.stories.tsx b/src/renderers/number-renderer/number-renderer.stories.tsx
new file mode 100644
index 00000000..5eac6918
--- /dev/null
+++ b/src/renderers/number-renderer/number-renderer.stories.tsx
@@ -0,0 +1,13 @@
+import {NumberRenderer} from './number-renderer';
+
+export default {
+ component: NumberRenderer,
+ title: 'Renderers/NumberRenderer',
+};
+
+export const integer = () => ;
+export const float = () => ;
+export const fixed = () => ;
+export const precision = () => ;
+export const nan = () => ;
+export const infinity = () => ;
diff --git a/src/renderers/number-renderer/number-renderer.tsx b/src/renderers/number-renderer/number-renderer.tsx
new file mode 100644
index 00000000..20ef5f8f
--- /dev/null
+++ b/src/renderers/number-renderer/number-renderer.tsx
@@ -0,0 +1,41 @@
+import {fi} from '@faker-js/faker';
+import React, {useMemo} from 'react';
+
+import {useLocale} from '../../core/locale';
+import {useContextWithDefaults} from '../../support';
+import {RendererProps} from '../types';
+
+export interface NumberRendererContextType {
+ fixed?: number;
+ precision?: number;
+}
+
+export const NumberRendererContext =
+ React.createContext({});
+
+export type NumberRendererProps = RendererProps<
+ number,
+ NumberRendererContextType
+>;
+
+export const NumberRenderer = ({value, ...rest}: NumberRendererProps) => {
+ const locale = useLocale();
+
+ const {fixed, precision} = useContextWithDefaults(
+ NumberRendererContext,
+ rest
+ );
+
+ const nf = useMemo(
+ () =>
+ new Intl.NumberFormat(locale.language, {
+ maximumFractionDigits: fixed,
+ maximumSignificantDigits: precision,
+ minimumFractionDigits: fixed,
+ minimumSignificantDigits: precision,
+ }),
+ [locale.language]
+ );
+
+ return {nf.format(value)};
+};
diff --git a/src/renderers/percent-renderer/index.tsx b/src/renderers/percent-renderer/index.tsx
new file mode 100644
index 00000000..aefee843
--- /dev/null
+++ b/src/renderers/percent-renderer/index.tsx
@@ -0,0 +1 @@
+export * from './percent-renderer';
diff --git a/src/renderers/percent-renderer/percent-renderer.stories.tsx b/src/renderers/percent-renderer/percent-renderer.stories.tsx
new file mode 100644
index 00000000..7ff91e2a
--- /dev/null
+++ b/src/renderers/percent-renderer/percent-renderer.stories.tsx
@@ -0,0 +1,19 @@
+import {PercentRenderer} from './percent-renderer';
+
+export default {
+ component: PercentRenderer,
+ title: 'Renderers/PercentRenderer',
+};
+
+export const fixed = () => (
+
+);
+export const precision = () => (
+
+);
+export const nan = () => ;
+export const infinity = () => ;
+export const percentZeroToOne = () => ;
+export const percentZeroToOneHundred = () => (
+
+);
diff --git a/src/renderers/percent-renderer/percent-renderer.tsx b/src/renderers/percent-renderer/percent-renderer.tsx
new file mode 100644
index 00000000..b9a612e1
--- /dev/null
+++ b/src/renderers/percent-renderer/percent-renderer.tsx
@@ -0,0 +1,46 @@
+import React, {useMemo} from 'react';
+
+import {useLocale} from '../../core/locale';
+import {useContextWithDefaults} from '../../support';
+import {NumberRendererContextType} from '../number-renderer';
+import {RendererProps} from '../types';
+
+export interface PercentRendererContextType extends NumberRendererContextType {
+ base?: 1 | 100;
+}
+
+export const PercentRendererContext =
+ React.createContext({});
+
+export type PercentRendererProps = RendererProps<
+ number,
+ PercentRendererContextType
+>;
+
+export const PercentRenderer = ({value, ...rest}: PercentRendererProps) => {
+ const locale = useLocale();
+
+ const {base, fixed, precision} = useContextWithDefaults(
+ PercentRendererContext,
+ rest
+ );
+
+ const nf = useMemo(
+ () =>
+ new Intl.NumberFormat(locale.language, {
+ maximumFractionDigits: fixed,
+ maximumSignificantDigits: precision,
+ minimumFractionDigits: fixed,
+ minimumSignificantDigits: precision,
+ style: 'percent',
+ }),
+ [fixed, locale.language, precision]
+ );
+
+ const formattedValue = useMemo(
+ () => (base === 100 ? value / 100 : value),
+ [base, value]
+ );
+
+ return {nf.format(formattedValue)};
+};
From f19d1a9ff4587b430a84bc5de2b40f141d4ab8fe Mon Sep 17 00:00:00 2001
From: ianwremmel <1182361+ianwremmel@users.noreply.github.com>
Date: Wed, 31 May 2023 09:00:54 -0700
Subject: [PATCH 4/4] feat: add Fact
---
src/data-display/fact/context.tsx | 16 +++++++++++
src/data-display/fact/fact-card.tsx | 21 ++++++++++++++
src/data-display/fact/fact.stories.tsx | 39 ++++++++++++++++++++++++++
src/data-display/fact/fact.tsx | 32 +++++++++++++++++++++
src/data-display/fact/index.ts | 3 ++
src/data-display/fact/types.ts | 8 ++++++
6 files changed, 119 insertions(+)
create mode 100644 src/data-display/fact/context.tsx
create mode 100644 src/data-display/fact/fact-card.tsx
create mode 100644 src/data-display/fact/fact.stories.tsx
create mode 100644 src/data-display/fact/fact.tsx
create mode 100644 src/data-display/fact/index.ts
create mode 100644 src/data-display/fact/types.ts
diff --git a/src/data-display/fact/context.tsx b/src/data-display/fact/context.tsx
new file mode 100644
index 00000000..7f8e78e8
--- /dev/null
+++ b/src/data-display/fact/context.tsx
@@ -0,0 +1,16 @@
+import {createContext} from 'react';
+
+import {AnyRenderer, Renderer as RendererType} from '../../renderers';
+
+import {FactCard} from './fact-card';
+import {FactContainer} from './types';
+
+export interface FactContextProps {
+ Container: FactContainer;
+ Renderer: RendererType;
+}
+
+export const FactContext = createContext({
+ Container: FactCard,
+ Renderer: AnyRenderer,
+});
diff --git a/src/data-display/fact/fact-card.tsx b/src/data-display/fact/fact-card.tsx
new file mode 100644
index 00000000..a84bf4be
--- /dev/null
+++ b/src/data-display/fact/fact-card.tsx
@@ -0,0 +1,21 @@
+import {Card} from 'react-bootstrap';
+
+import {FactContainer} from './types';
+
+/**
+ * Default container for the Fact component
+ *
+ * Note that this uses Bootstrap's Card directly rather than the local wrapper.
+ * The local wrapper does things with headings that may or may not be
+ * appropriate at this time.
+ */
+export const FactCard: FactContainer = ({label, output}) => (
+
+
+ {label}
+
+
+ {output}
+
+
+);
diff --git a/src/data-display/fact/fact.stories.tsx b/src/data-display/fact/fact.stories.tsx
new file mode 100644
index 00000000..a1a80e5a
--- /dev/null
+++ b/src/data-display/fact/fact.stories.tsx
@@ -0,0 +1,39 @@
+import {useContext} from 'react';
+import {Card} from 'react-bootstrap';
+
+import {CurrencyRenderer} from '../../renderers/currency-renderer';
+
+import {FactContext} from './context';
+import {Fact} from './fact';
+import {FactContainer} from './types';
+
+export default {
+ component: Fact,
+ title: 'Data Display/Fact',
+};
+
+export const bigNumber = () => (
+
+);
+
+export const word = () => ;
+
+export const currency = () => (
+
+);
+
+const FooterFactCard: FactContainer = ({label, output}) => (
+
+ {output}
+ {label}
+
+);
+
+export const AlternateContainer = () => {
+ const defaults = useContext(FactContext);
+ return (
+
+
+
+ );
+};
diff --git a/src/data-display/fact/fact.tsx b/src/data-display/fact/fact.tsx
new file mode 100644
index 00000000..a3147a0b
--- /dev/null
+++ b/src/data-display/fact/fact.tsx
@@ -0,0 +1,32 @@
+import {ReactNode} from 'react';
+
+import {Renderer as RendererType} from '../../renderers';
+import {useContextWithDefaults} from '../../support';
+
+import {FactContext, FactContextProps} from './context';
+
+export interface FactProps
+ extends Partial> {
+ label: ReactNode;
+ value: T;
+ Renderer?: RendererType;
+}
+
+export const Fact = ({
+ label,
+ value,
+ Renderer: OverrideRenderer,
+ ...rest
+}: FactProps) => {
+ const {Container, Renderer: DefaultRenderer} = useContextWithDefaults(
+ FactContext,
+ rest
+ );
+ const Renderer = OverrideRenderer ?? DefaultRenderer;
+
+ return (
+ <>
+ }>
+ >
+ );
+};
diff --git a/src/data-display/fact/index.ts b/src/data-display/fact/index.ts
new file mode 100644
index 00000000..16ccf936
--- /dev/null
+++ b/src/data-display/fact/index.ts
@@ -0,0 +1,3 @@
+export * from './fact';
+export * from './fact-card';
+export * from './types';
diff --git a/src/data-display/fact/types.ts b/src/data-display/fact/types.ts
new file mode 100644
index 00000000..b2654deb
--- /dev/null
+++ b/src/data-display/fact/types.ts
@@ -0,0 +1,8 @@
+import {ComponentType, ReactNode} from 'react';
+
+export interface FactContainerProps {
+ label: ReactNode;
+ output: ReactNode;
+}
+
+export type FactContainer = ComponentType;