Skip to content

Commit

Permalink
Merge pull request #1332 from okta/ab/document-tokens
Browse files Browse the repository at this point in the history
Ab/document tokens
  • Loading branch information
andrewberg-okta authored Feb 9, 2022
2 parents 107cd6c + 3edfdbe commit 61df89b
Show file tree
Hide file tree
Showing 34 changed files with 701 additions and 300 deletions.
78 changes: 78 additions & 0 deletions packages/odyssey-storybook/.storybook/components/ThemeTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*!
* Copyright (c) 2022-present, Okta, Inc. and/or its affiliates. All rights reserved.
* The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.")
*
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and limitations under the License.
*/

/* eslint-disable import/no-extraneous-dependencies */
import React from "react";
import type { Theme, ThemeReducer } from "@okta/odyssey-react-theme";
import { tokens } from "@okta/odyssey-react-theme/dist/ThemeProvider/context";
import type { ThemeKey } from "@okta/odyssey-react-theme/dist/ThemeProvider/context";
import { Table, Text } from "@okta/odyssey-react";
/* eslint-enable import/no-extraneous-dependencies */
import type { ReactNode } from "react";

type ThemeTableProps = {
componentThemeReducer: ThemeReducer;
};

const ThemeTable = ({ componentThemeReducer }: ThemeTableProps): ReactNode => {
const remappedTokens: { [key: string]: string } = {};
for (const [k] of Object.entries(tokens)) {
remappedTokens[k] = k;
}
const componentVariables = componentThemeReducer(
remappedTokens as unknown as Theme
);

function getTokenValue(tokenName: string | number): string | number | null {
let tokenValue = null;
let p: ThemeKey;
for (p in tokens) {
if (p === tokenName) {
tokenValue = tokens[p];
}
}
return tokenValue;
}

return (
<Table caption="Theme Variables" screenReaderCaption="Themeing variables">
<Table.Header>
<Table.Row>
<Table.HeaderCell scope="col">Variable Name</Table.HeaderCell>
<Table.HeaderCell scope="col">Token Value</Table.HeaderCell>
<Table.HeaderCell scope="col" format="num"></Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{Object.keys(componentVariables).map((variable) => (
<Table.Row key={`${variable}-row`}>
<Table.DataCell>
<Text as="code">{variable}</Text>
</Table.DataCell>
<Table.DataCell>
<Text as="code">{componentVariables[variable]}</Text>
</Table.DataCell>
<Table.DataCell format="num">
<Text as="code" color="sub" fontSize="caption">
{getTokenValue(componentVariables[variable])
? `(${getTokenValue(componentVariables[variable])})`
: ""}
</Text>
</Table.DataCell>
</Table.Row>
))}
</Table.Body>
</Table>
);
};

export { ThemeTable };
240 changes: 240 additions & 0 deletions packages/odyssey-storybook/.storybook/components/TokenTables.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
/*!
* Copyright (c) 2022-present, Okta, Inc. and/or its affiliates. All rights reserved.
* The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.")
*
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and limitations under the License.
*/

/* eslint-disable import/no-extraneous-dependencies */
import React, { useState, useEffect } from "react";
import * as Tokens from "@okta/odyssey-design-tokens";
import { Table, Text } from "@okta/odyssey-react";
/* eslint-enable import/no-extraneous-dependencies */
import type { ReactNode } from "react";

type FontWeight = 400 | 600;

type Token = {
name: string;
value: string | number;
};

type TableData = {
name: string;
values: Array<Token>;
};

type TokenName = keyof typeof Tokens;

const TokenTables = (): ReactNode => {
const [tables, setTables] = useState<Array<TableData>>([]);

useEffect(() => {
const tokenKeys = Object.keys(Tokens);
const tempTables: Array<TableData> = [];
let currentType: string;
let currentValues: Array<Token>;
tokenKeys.forEach((tokenName) => {
const parts = tokenName.match(/[A-Z][a-z]+/g);
if (!parts) return;

let tokenType = parts[0];
if (parts[0] === "Color") {
tokenType = `${parts[1]} ${parts[0]}s`;
}

if (tokenType !== currentType) {
currentValues = [];
tempTables.push({ name: tokenType, values: currentValues });
currentType = tokenType;
}

currentValues.push({
name: tokenName,
value: Tokens[tokenName as TokenName],
});
});

tempTables
.find((table) => table.name === "Palette Colors")
?.values.sort((a: Token, b: Token) => {
const nameA = a.name.toUpperCase();
const nameB = b.name.toUpperCase();
if (nameA < nameB) {
return -1;
}
if (nameA > nameB) {
return 1;
}
return 0;
});

setTables(tempTables);
}, [setTables]);

const renderBorder = (token: Token) => (
<span
style={{
width: "1.25em",
height: "1.25em",
borderWidth: token.name.includes("BorderWidth") ? token.value : "1px",
borderStyle: token.name.includes("BorderStyle")
? (token.value as string)
: "solid",
borderColor: "#d7d7dc",
borderRadius: `${
token.name.includes("BorderRadius") ? token.value : ""
}`,
display: "inline-block",
verticalAlign: "middle",
}}
></span>
);

const renderColor = (val: string | number) => (
<span
style={{
width: "1em",
height: "1em",
display: "inline-block",
backgroundColor: `${val}`,
borderStyle: "solid",
borderColor: `${val === "#ffffff" ? "#d7d7dc" : "transparent"}`,
borderRadius: "4px",
verticalAlign: "middle",
}}
></span>
);

const renderColorText = (val: string | number) => (
<span
style={{
color: `${val}`,
display: "inline-block",
paddingInline: "4px",
backgroundColor: `${val === "#ffffff" ? "#000" : ""}`,
}}
>
Aa
</span>
);

const renderSpace = (val: string | number) => (
<span
style={{
width: `${val}`,
height: `${val}`,
display: "inline-block",
backgroundColor: "#f88c90",
}}
></span>
);

const renderFont = (token: Token) => (
<span
style={{
fontFamily: `${token.name.includes("FontFamily") ? token.value : ""}`,
fontSize: `${token.name.includes("FontSize") ? token.value : ""}`,
fontWeight: `${
token.name.includes("FontWeight") ? token.value : ""
}` as unknown as FontWeight,
lineHeight: `${token.name.includes("LineHeight") ? token.value : ""}`,
backgroundColor: `${
token.name.includes("LineHeight") ? "#ebebed" : ""
}`,
display: `${token.name.includes("LineHeight") ? "inline-block" : ""}`,
}}
>
Waltz, bad nymph, for quick jigs vex!
</span>
);

const renderFocus = (val: string | number) => (
<span
style={{
width: "1.25em",
height: "1.25em",
display: "inline-block",
boxShadow: `0 0 0 ${val} #a7b5ec`,
}}
></span>
);

const renderShadow = (val: string | number) => (
<span
style={{
width: "1.5em",
height: "1.5em",
display: "inline-block",
boxShadow: `${val}`,
}}
></span>
);

function getDisplayedValue(tableName: string, val: string | number) {
if (!tableName.includes("Colors") || tableName === "Palette Colors") {
return val;
}
const tokenValue = tables
.find((table) => table.name === "Palette Colors")
?.values.find((value) => value.value === val) ?? { name: "" };
return tokenValue.name;
}

return (
<>
{tables.map((table) => (
<Table
screenReaderCaption={"Design tokens"}
caption={table.name}
key={table.name}
>
<Table.Header>
<Table.Row>
<Table.HeaderCell scope="col">Token Name</Table.HeaderCell>
<Table.HeaderCell scope="col">Example</Table.HeaderCell>
<Table.HeaderCell scope="col">Value</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{table.values.map((token: Token) => (
<Table.Row key={`${token.name}-row`}>
<Table.DataCell>
<Text as="code">{token.name}</Text>
</Table.DataCell>
<Table.DataCell>
{token.name.includes("Border") &&
!token.name.includes("ColorBorder") &&
renderBorder(token)}
{token.name.includes("Color") &&
!token.name.includes("ColorText") &&
renderColor(token.value)}
{token.name.includes("ColorText") &&
renderColorText(token.value)}
{token.name.includes("Space") && renderSpace(token.value)}
{token.name.includes("Font") && renderFont(token)}
{token.name.includes("Focus") &&
!token.name.includes("ColorFocus") &&
renderFocus(token.value)}
{token.name.includes("Shadow") && renderShadow(token.value)}
</Table.DataCell>
<Table.DataCell>
<Text as="code">
{getDisplayedValue(table.name, token.value)}
</Text>
</Table.DataCell>
</Table.Row>
))}
</Table.Body>
</Table>
))}
</>
);
};

export { TokenTables };
18 changes: 14 additions & 4 deletions packages/odyssey-storybook/src/components/Banner/Banner.mdx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Canvas, Story, ArgsTable } from "@storybook/addon-docs";
import { theme } from "@okta/odyssey-react/dist/components/Banner/Banner.theme";
import { ThemeTable } from "../../../.storybook/components/ThemeTable";

# Banner

Expand All @@ -20,23 +22,29 @@ They are best used to indicate global states or errors that effect an entire pro

Use Info Banners to surface neutral information or broad announcements to users.

<Story id="components-banner--info" />
<Canvas>
<Story id="components-banner--info" />
</Canvas>

### Danger

Use Danger Banners to inform users that a system-wide error has occurred. You may also inform a user when access is about to be lost.

Include guidance to make sure users know what steps to take to address the error or avoid the threat.

<Story id="components-banner--danger" />
<Canvas>
<Story id="components-banner--danger" />
</Canvas>

### Caution

Use Caution Banners to inform users of tasks or processes that may need their attention.

When using the Caution variant, ensure the user does not need more context than you can give in the space available.

<Story id="components-banner--caution" />
<Canvas>
<Story id="components-banner--caution" />
</Canvas>

## Usage

Expand Down Expand Up @@ -68,10 +76,12 @@ Banners support dismissal for messages that do not persist or only require a one

Banner content should be succinct and direct. When including an action, be sure the link text clearly indicates where it leads.

## Arguments
## Props

<ArgsTable />

<ThemeTable componentThemeReducer={theme} />

## References

<figure class="ods-table--figure">
Expand Down
4 changes: 4 additions & 0 deletions packages/odyssey-storybook/src/components/Box/Box.mdx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Canvas, Story, ArgsTable } from "@storybook/addon-docs";
import { theme } from "@okta/odyssey-react/dist/components/Box/Box.theme";
import { ThemeTable } from "../../../.storybook/components/ThemeTable";

# Box

Expand All @@ -20,6 +22,8 @@ A simple `<Button>` component might look like this:

<ArgsTable />

<ThemeTable componentThemeReducer={theme} />

## Behavior

Box uses an `as="abbr"` polymorphic API to ensure semantic elements can be used as necessary. This helps improve the quality of the resulting DOM output. This improves outcomes for accessibility, testing, use-ability, and long term maintenance.
Expand Down
Loading

0 comments on commit 61df89b

Please sign in to comment.