Remove docs package (#178)
* Remove all docs content from fluentui-react-native * add headline links to documentation * update yarn.lock * update yarn.lock for merge * re-add images to new assets dir for repo readmes to reference * refresh yarn.lock * refresh yarn.lock cleanly after mege * update yarn.lock Co-authored-by: Chris Hogan <chrishog@microsoft.com>
This commit is contained in:
Родитель
b94dc570ad
Коммит
b1ec056884
26
README.md
26
README.md
|
@ -8,8 +8,8 @@ If you have an existing React Native project, it's easy to begin using FluentUI
|
|||
|
||||
### Prerequisites
|
||||
|
||||
* [Standard React Native dependencies](http://facebook.github.io/react-native/docs/getting-started.html#node-python2-jdk)
|
||||
* [Node.js](https://nodejs.org/en/download/)
|
||||
- [Standard React Native dependencies](http://facebook.github.io/react-native/docs/getting-started.html#node-python2-jdk)
|
||||
- [Node.js](https://nodejs.org/en/download/)
|
||||
|
||||
### Install FluentUI React Native into an existing project
|
||||
|
||||
|
@ -25,7 +25,25 @@ After successful installation, you can test the package by importing components
|
|||
import { Checkbox } from '@fluentui/react-native';
|
||||
```
|
||||
|
||||
Once you have the package installed, check out our [Hello World Fluent page](./docs/pages/HELLO_WORLD.md) to start writing code (Coming Soon).
|
||||
Once you have the package installed, check out our [Hello World Fluent page](https://github.com/microsoft/fluent-site/blob/master/packages/fluent-website/docs/windows/get-started/Hello-World.mdx) to start writing code (Coming Soon).
|
||||
|
||||
## Documentation
|
||||
|
||||
### Components and Controls
|
||||
|
||||
Our component documentation is hosted in a separate repository, [Microsoft FluentUI Site](https://github.com/Microsoft/fluent-site) that will be published to its own website (Coming Soon).
|
||||
|
||||
### Theming framework
|
||||
|
||||
Our FluentUI framework documentation is found in this repository alongside the implementation.
|
||||
|
||||
- [Theming Overview](./packages/framework/theming-react-native/README.md)
|
||||
- [StyleSheets](./packages/framework/themed-stylesheet/README.md)
|
||||
- [Customizing Theme Settings](./packages/framework/themed-settings/README.md)
|
||||
- [Theme Registry](./packages/framework/theme-registry/README.md)
|
||||
- [Tokens](./packages/framework/foundation-tokens/README.md)
|
||||
- [Settings and Slots](./packages/framework/foundation-settings/README.md)
|
||||
- [Compose](./packages/framework/foundation-compose/README.md) and [Composable](./packages/framework/foundation-composable/README.md)
|
||||
|
||||
## Developing in the repo
|
||||
|
||||
|
@ -45,7 +63,9 @@ To start developing in the repository you can:
|
|||
After a successful yarn build, you can explore FluentUI Tester, our demo application to play with each of the controls. To run FluentUI Tester, please follow instructions in the [FluentUI Tester readme](./apps/fluent-tester/README.md).
|
||||
|
||||
### Beachball
|
||||
|
||||
This repo manages semantic versioning and publishing using [Beachball](https://github.com/microsoft/beachball). When contributing, make sure to run the following before making a pull request:
|
||||
|
||||
1. `yarn change` will take you through a command line wizard to generate change files
|
||||
2. Make sure to commit and push the newly generated change file
|
||||
|
||||
|
|
|
@ -21,9 +21,10 @@
|
|||
```
|
||||
yarn run-win32
|
||||
```
|
||||
|
||||
5. You will see FluentUI Tester show up in a new window.
|
||||
|
||||
![ReactTest image debug menu location](./../../docs/pages/images/fluent_tester_radiogroup.png)
|
||||
![ReactTest image debug menu location](./../../assets/fluent_tester_radiogroup.png)
|
||||
|
||||
## Debug `FluentUI Tester` app with direct debugging
|
||||
|
||||
|
@ -46,11 +47,11 @@ Note: we recommend using [Visual Studio Code](https://code.visualstudio.com/down
|
|||
|
||||
5. Inside ReactTest, open the debug option menu and select the checkbox `Use Direct Debugger`
|
||||
|
||||
![ReactTest image debug menu location](./../../docs/pages/images/fluent_tester_debug_menu.png)
|
||||
![ReactTest image debug menu location](./../../assets/fluent_tester_debug_menu.png)
|
||||
|
||||
6. In Visual Studio Code, open the debug pane and select `Debug Fabric Tester` option from the "Run And Debug" dropdown.
|
||||
|
||||
![ReactTest image debug menu location](./../../docs/pages/images/fluent_tester_vscode_debug.png)
|
||||
![ReactTest image debug menu location](./../../assets/fluent_tester_vscode_debug.png)
|
||||
|
||||
7. At this time, VS Code will attach to the JS runtime and you can start debugging. For more information on debugging in VS Code, please see [Visual Studio Code documentation](https://code.visualstudio.com/docs/editor/debugging).
|
||||
|
||||
|
|
До Ширина: | Высота: | Размер: 14 KiB После Ширина: | Высота: | Размер: 14 KiB |
До Ширина: | Высота: | Размер: 14 KiB После Ширина: | Высота: | Размер: 14 KiB |
До Ширина: | Высота: | Размер: 11 KiB После Ширина: | Высота: | Размер: 11 KiB |
|
@ -1,110 +0,0 @@
|
|||
{
|
||||
"name": "fluent-rn-website",
|
||||
"entries": [
|
||||
{
|
||||
"date": "Fri, 17 Apr 2020 22:36:03 GMT",
|
||||
"tag": "fluent-rn-website_v0.3.0",
|
||||
"version": "0.3.0",
|
||||
"comments": {
|
||||
"minor": [
|
||||
{
|
||||
"comment": "yarn.lock",
|
||||
"author": "warleu@microsoft.com",
|
||||
"commit": "205e7b7e88151a17f2560ecdaec1c6e13c5825b7",
|
||||
"package": "fluent-rn-website"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Fri, 17 Apr 2020 16:48:18 GMT",
|
||||
"tag": "fluent-rn-website_v0.2.2",
|
||||
"version": "0.2.2",
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"comment": "Revert \"Merge pull request #136 from ksiler/fluent-variant-support\"",
|
||||
"author": "krsiler@microsoft.com",
|
||||
"commit": "302097d2dc2dc50680be2ff3747b9c244501d7d5",
|
||||
"package": "fluent-rn-website"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Tue, 14 Apr 2020 05:14:46 GMT",
|
||||
"tag": "fluent-rn-website_v0.2.0",
|
||||
"version": "0.2.0",
|
||||
"comments": {
|
||||
"minor": [
|
||||
{
|
||||
"comment": "add accessibility documentation",
|
||||
"author": "krsiler@microsoft.com",
|
||||
"commit": "232a97794dc044cec9671e5cca4cf204d881f614",
|
||||
"package": "fluent-rn-website"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Mon, 13 Apr 2020 22:48:23 GMT",
|
||||
"tag": "fluent-rn-website_v0.1.0",
|
||||
"version": "0.1.0",
|
||||
"comments": {
|
||||
"minor": [
|
||||
{
|
||||
"comment": "add docs for text",
|
||||
"author": "krsiler@microsoft.com",
|
||||
"commit": "232a97794dc044cec9671e5cca4cf204d881f614",
|
||||
"package": "fluent-rn-website"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Thu, 09 Apr 2020 18:39:15 GMT",
|
||||
"tag": "fluent-rn-website_v0.0.4",
|
||||
"version": "0.0.4",
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"comment": "merge conflicts",
|
||||
"author": "ppatboyd@outlook.com",
|
||||
"commit": "232a97794dc044cec9671e5cca4cf204d881f614",
|
||||
"package": "fluent-rn-website"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Thu, 09 Apr 2020 17:35:20 GMT",
|
||||
"tag": "fluent-rn-website_v0.0.3",
|
||||
"version": "0.0.3",
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"comment": "add some dependencies",
|
||||
"author": "mgodbolt@microsoft.com",
|
||||
"commit": "232a97794dc044cec9671e5cca4cf204d881f614",
|
||||
"package": "fluent-rn-website"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Wed, 08 Apr 2020 21:26:12 GMT",
|
||||
"tag": "fluent-rn-website_v0.0.2",
|
||||
"version": "0.0.2",
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"comment": "update package name",
|
||||
"author": "mgodbolt@microsoft.com",
|
||||
"commit": "232a97794dc044cec9671e5cca4cf204d881f614",
|
||||
"package": "fluent-rn-website"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
# Change Log - fluent-rn-website
|
||||
|
||||
This log was last generated on Fri, 17 Apr 2020 22:36:03 GMT and should not be manually modified.
|
||||
|
||||
<!-- Start content -->
|
||||
|
||||
## 0.3.0
|
||||
|
||||
Fri, 17 Apr 2020 22:36:03 GMT
|
||||
|
||||
### Minor changes
|
||||
|
||||
- yarn.lock (warleu@microsoft.com)
|
||||
|
||||
## 0.2.2
|
||||
|
||||
Fri, 17 Apr 2020 16:48:18 GMT
|
||||
|
||||
### Patches
|
||||
|
||||
- Revert "Merge pull request #136 from ksiler/fluent-variant-support" (krsiler@microsoft.com)
|
||||
|
||||
## 0.2.0
|
||||
|
||||
Tue, 14 Apr 2020 05:14:46 GMT
|
||||
|
||||
### Minor changes
|
||||
|
||||
- add accessibility documentation (krsiler@microsoft.com)
|
||||
|
||||
## 0.1.0
|
||||
Mon, 13 Apr 2020 22:48:23 GMT
|
||||
|
||||
### Minor changes
|
||||
|
||||
- add docs for text (krsiler@microsoft.com)
|
||||
## 0.0.4
|
||||
Thu, 09 Apr 2020 18:39:15 GMT
|
||||
|
||||
### Patches
|
||||
|
||||
- merge conflicts (ppatboyd@outlook.com)
|
||||
## 0.0.3
|
||||
Thu, 09 Apr 2020 17:35:20 GMT
|
||||
|
||||
### Patches
|
||||
|
||||
- add some dependencies (mgodbolt@microsoft.com)
|
||||
## 0.0.2
|
||||
Wed, 08 Apr 2020 21:26:12 GMT
|
||||
|
||||
### Patches
|
||||
|
||||
- update package name (mgodbolt@microsoft.com)
|
106
docs/README.md
106
docs/README.md
|
@ -1,106 +0,0 @@
|
|||
# Fluent Website Content
|
||||
|
||||
__Key Concepts:__
|
||||
1. [Folder path === URL](#adding-new-content)
|
||||
2. [Left hand navigation defined per folder/folder tree](#creating-navigation)
|
||||
3. [Built with MD, MDX or TSX](#supported-page-formats)
|
||||
4. [Host your content anywhere](#hosting-your-content)
|
||||
|
||||
## Adding new content
|
||||
|
||||
Files in the `docs` folders are built to a page with the same URL as the relative directory. `index` files will be rendered as the folder's root page.
|
||||
|
||||
`docs/components/button.mdx` will be built to `example.com/components/button.html`. `docs/styles/index.tsx` will be built to `example.com/styles/index.html`
|
||||
|
||||
|
||||
## Creating navigation
|
||||
|
||||
The vertical navigation of each page is written in a `toc.yml` file that includes `name`, `link` and any children `items`.
|
||||
|
||||
- `name` is the link text
|
||||
- `link` is the full url to the page
|
||||
- `items` is an array of name/link pairs and can be further nested
|
||||
|
||||
|
||||
```yml
|
||||
- name: Components
|
||||
items:
|
||||
- name: Button
|
||||
link: components/button
|
||||
- name: Toggle
|
||||
link: components/toggle
|
||||
|
||||
```
|
||||
|
||||
### Unique navigation for sub controls
|
||||
|
||||
Often you'll want a subsection of the site to have its own navigation. The navigation of each page is based off of the closest `toc.yml` file to the page.
|
||||
|
||||
```md
|
||||
docs/
|
||||
styles.mdx
|
||||
toc.yml
|
||||
components/
|
||||
button.mdx
|
||||
toc.yml
|
||||
foo/
|
||||
bar.mdx
|
||||
|
||||
```
|
||||
|
||||
The `styles` page will have the navigation from `docs/toc.yml` and `button` page will use the navigation found in `docs/components/toc.yml`.
|
||||
|
||||
`docs/foo` does not contain a `toc.yml` so `docs/toc.yml` will be used for `bar.mdx`.
|
||||
|
||||
## Supported page formats
|
||||
|
||||
The Fluid UI Site supports multiple page formats.
|
||||
|
||||
### MDX
|
||||
|
||||
[MDX](https://mdxjs.com/) is a superset of markdown that adds the power of JSX to the file.
|
||||
This means you can import JSX directly into your markdown content.
|
||||
|
||||
#### Importing JSX into MDX
|
||||
|
||||
```md
|
||||
import {Button} from 'office-ui-fabric-react'
|
||||
|
||||
## This is a Fabric button
|
||||
|
||||
<Button primary={true}> Click Me </Button>
|
||||
|
||||
```
|
||||
|
||||
#### Importing MD into MDX
|
||||
|
||||
Another great feature of MDX is the ability to import other MD or MDX files into a single file.
|
||||
This is a great way to split content out into multiple files and combine/reuse it.
|
||||
|
||||
```md
|
||||
import Stuff from './somestuff.md'
|
||||
|
||||
Hello, this is my <Stuff />
|
||||
```
|
||||
|
||||
### TSX Files
|
||||
|
||||
TSX files can be used when you need complete control over the page contents. No assumptions will be made about the page contents, styles or meta information (other than URL).
|
||||
|
||||
|
||||
#### Leveraging site templates
|
||||
|
||||
Unless your page is meant to be a standalone app, we recommend using the built in `PageTemplate` to render the default page shell.
|
||||
|
||||
```tsx
|
||||
import React from 'react';
|
||||
import PageTemplate from 'gatsby-theme-fluent-site/src/templates/PageTemplate'
|
||||
import
|
||||
|
||||
export default () => {
|
||||
return <PageTemplate>Page Content</PageTemplate>
|
||||
}
|
||||
```
|
||||
## Hosting your content
|
||||
|
||||
Gatsby can source pages from multiple locations. Content added to this repo under `docs/ios` could easily be moved to another repo under `fluentui-docs/ios` and produce the exact same page content. This workflow is not yet fully implemented, but it is a core tenent and fully supported by our tech choices.
|
|
@ -1,2 +0,0 @@
|
|||
- name: Windows
|
||||
link: /windows
|
|
@ -1,100 +0,0 @@
|
|||
---
|
||||
titleCategory: Design & developer
|
||||
title: Callout
|
||||
---
|
||||
|
||||
import { Usage, Example } from 'gatsby-theme-fluent-site/src/components/Usage';
|
||||
import { NYI } from 'gatsby-theme-fluent-site/src/components/NYI';
|
||||
import { Playground } from 'gatsby-theme-fluent-site/src/components/Playground';
|
||||
|
||||
<style>{`
|
||||
.fakefocus {
|
||||
outline: 2px solid black;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
`}</style>
|
||||
|
||||
## Overview
|
||||
|
||||
Callouts are a powerful way to simplify a user interface. They host tips and other information users need when they need it, with minimal effort on their part. Callouts can help you use screen space more effectively and reduce screen clutter. However, poorly designed Callouts can be annoying, distracting, unhelpful, overwhelming, or in the way.
|
||||
|
||||
Use a Callout for displaying additional contextual information about an item on the screen. Callouts also have a tail that identifies their source. A common use for Callout is the introduction of a new feature or capability of an app or site. Alternate usages include pairing the Callout with a button or clickable element for on-demand presentation of additional or supporting content.
|
||||
|
||||
By default, Callouts that do not contain focusable elements (links, buttons etc) cannot gain focus when opened. For proper screen reader support, follow the non-focusable callout example, which treats the callout content like a status message.
|
||||
|
||||
[Design spec]() |
|
||||
[GitHub](https://github.com/microsoft/fluentui-react-native/tree/master/packages/components/Callout)
|
||||
|
||||
export const themes = ['Light', 'Dark'];
|
||||
export const examples = [
|
||||
{
|
||||
title: 'Simple Callout',
|
||||
description: "We don't have FluentUI working in the examples yet.",
|
||||
source: 'export default () => <Callout><Text>React Native Callout control</Text></Callout>'
|
||||
}
|
||||
];
|
||||
|
||||
<Playground themes={themes} examples={examples} />
|
||||
|
||||
## Best practices
|
||||
|
||||
<NYI>Content needed: best practices</NYI>
|
||||
|
||||
### Usage
|
||||
|
||||
<NYI>Content needed: usage examples</NYI>
|
||||
|
||||
### Layout
|
||||
|
||||
<NYI>Content needed: positioning, anchoring</NYI>
|
||||
|
||||
### Accessibility
|
||||
|
||||
<NYI>Content needed: explain specifics of ARIA properties; mind 'announce' in particular</NYI>
|
||||
|
||||
### Globalization
|
||||
|
||||
<NYI>Content needed: any int'l concerns</NYI>
|
||||
|
||||
## How to customize
|
||||
|
||||
<NYI>Content needed: add a customization example</NYI>
|
||||
|
||||
You can customize the appearance of this and other controls by overriding its _design tokens_. Controls in your UI should be consistent in appearance, so most changes to a control's appearance should affect _all_ controls of the same type in your app. If you need a one-off style, however, you can override the design tokens for a single instance of a control.
|
||||
|
||||
- [Overview: How to use design tokens](/web/styles)
|
||||
|
||||
Each control uses many different design tokens. To explore the design tokens that affect this control, select "Design tokens" in the the playground at the top of this page. <NYI />
|
||||
|
||||
## Related components
|
||||
|
||||
<NYI>Content needed</NYI>
|
||||
|
||||
## In-depth examples
|
||||
|
||||
<NYI>Content needed</NYI>
|
||||
|
||||
### Additional examples
|
||||
|
||||
<NYI>Content needed</NYI>
|
||||
<NYI>Explain the actual process of overriding design tokens (which doesn't exist yet)</NYI>
|
||||
|
||||
## Implementation status
|
||||
|
||||
This control is **unfinished**. The table below shows the current status of the implementation.
|
||||
|
||||
✓ Design specifications and UI kits
|
||||
|
||||
✓ Theming and spacing
|
||||
|
||||
✓ Motion
|
||||
|
||||
✓ Design tokens
|
||||
|
||||
✓ Responsive
|
||||
|
||||
### Issues
|
||||
|
||||
#### Is this page helpful?
|
||||
|
||||
👍 Yes 👎 No
|
|
@ -1,59 +0,0 @@
|
|||
# Component Name: Checkbox
|
||||
|
||||
## Purpose:
|
||||
The goal of this Checkbox component is to allows users to switch between two mutually exclusive options (checked or unchecked,
|
||||
on or off) through a single click or tap. It can also be used to indicate a subordinate setting or preference when paired with another control.
|
||||
|
||||
## Do's:
|
||||
- Allow users to choose any combination of options when several Checkboxes are grouped together.
|
||||
|
||||
## Don't:
|
||||
- Don't use a Checkbox as an on/off control. Instead use a toggle switch.
|
||||
- Don’t use a Checkbox when the user can choose only one option from the group, use radio buttons instead.
|
||||
- Don't put two groups of Checkboxes next to each other. Separate the two groups with labels.
|
||||
|
||||
## Sample Code:
|
||||
```
|
||||
<Checkbox label="This is the label (uncontrolled)" onChange={onChange} defaultChecked={false} />
|
||||
<Checkbox label="This is a controlled Checkbox" onChange={onChangeControlled1} checked={isCheckedControlled1} />
|
||||
```
|
||||
|
||||
## Tokens:
|
||||
Checkbox supports the following tokens:
|
||||
1. checkboxBackgroundColor – This changes the background color of the Checkbox.
|
||||
2. checkboxBorderColor – This changes the border color of the Checkbox.
|
||||
3. checkmarkColor – This changes the color of Checkmark.
|
||||
4. borderRadius - This changes the border radius of the Checkbox (use this to create a circular checkbox)
|
||||
|
||||
## Token Usage Example:
|
||||
Circular Checkbox: We use "borderRadius=7" right now because we currently don't support % for borderRadius. The checkbox
|
||||
size is currently 14x14, so 7 is 50%. We have a task to allow for %'s.
|
||||
```
|
||||
const CircularCheckbox = Checkbox.customize({ tokens: { borderRadius: 7 } });
|
||||
```
|
||||
|
||||
Checkbox with white background (when unchecked):
|
||||
```
|
||||
const WhiteCheckbox = Checkbox.customize({ tokens: { backgroundColor: 'white' } });
|
||||
```
|
||||
|
||||
Circular Color-Customized Checkbox - (Green background + green border + white checkmark) when Checked.
|
||||
```
|
||||
const CircleColorCheckbox = Checkbox.customize({
|
||||
tokens: { borderRadius: 7 },
|
||||
_overrides: {
|
||||
checked: {
|
||||
tokens: {
|
||||
checkboxBackgroundColor: 'green',
|
||||
checkboxBorderColor: 'green',
|
||||
checkmarkColor: 'white'
|
||||
}
|
||||
},
|
||||
focused: { tokens: { checkboxBackgroundColor: 'menuItemBackgroundHovered' } },
|
||||
hovered: { tokens: { checkboxBackgroundColor: 'menuItemBackgroundHovered' } },
|
||||
pressed: { tokens: { checkboxBackgroundColor: 'menuItemBackgroundPressed' } }
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
# Button Documentation
|
||||
|
||||
## Example
|
||||
|
||||
```
|
||||
import * as React from 'react';
|
||||
import { Button, View } from '@fluentui/react-native';
|
||||
|
||||
export const ButtonExample: React.FunctionComponent<{}> = props => {
|
||||
return (
|
||||
<View>
|
||||
<Button content="Save" onClick={_alertClicked} />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
function _alertClicked(): void {
|
||||
alert('Clicked');
|
||||
}
|
||||
```
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
title: Components
|
||||
---
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
# Link
|
||||
|
||||
## Example
|
||||
|
||||
```
|
||||
import * as React from 'react';
|
||||
import * as ReactNative from 'react-native';
|
||||
import { Stack } from '../components';
|
||||
import { Link } from '../components/Link/Link.win32';
|
||||
import { stackStyle } from './TesterStyles';
|
||||
|
||||
export const LinkTest: React.FunctionComponent<{}> = () => {
|
||||
const doPress = () => {
|
||||
ReactNative.Alert.alert('Alert.', 'You have been alerted.');
|
||||
};
|
||||
return (
|
||||
<Stack style={stackStyle}>
|
||||
<Link url="https://www.bing.com/" content="Click to open the URL." />
|
||||
<Link onPress={doPress} content="Click to activate the onPress event." />
|
||||
<Link url="https://www.google.com/" content="This link is disabled." disabled />
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
```
|
|
@ -1,39 +0,0 @@
|
|||
# Component Name: RadioGroup
|
||||
|
||||
## Purpose
|
||||
The goal of this RadioGroup component is to let users select one option from two or more choices. Each option is represented by one RadioButton component, and the group of RadioButton’s is represented by a RadioGroup component. A user can only select one RadioButton in a RadioGroup.
|
||||
|
||||
## Do's:
|
||||
- Use when there are 2-7 options, if you have enough screen space and the options are important enough to be a good use of that screen space. Otherwise, use a Checkbox or Dropdown list.
|
||||
- Use on wizard pages to make the alternatives clear, even if a Checkbox is otherwise acceptable.
|
||||
- List the options in a logical order, such as most likely to be selected least, simplest operation to most complex, or least risk to most. Alphabetical ordering is not recommended because it is language dependent and therefore not localizable.
|
||||
- If none of the options is a valid choice, add another option to reflect this choice, such as “None” or “Does not apply”.
|
||||
- Select the safest (to prevent loss of data or system access) and most secure and private option as the default. If safety and security aren’t factors, select the most likely or convenient option.
|
||||
- Align radio buttons vertically instead of horizontally, if possible. Horizontal alignment is harder to read and localize.
|
||||
|
||||
## Don't:
|
||||
- Use when the options are numbers that have fixed steps, like 10, 20, 30. Use a slider component instead.
|
||||
- Use if there are more than 7 options, use a Dropdown instead.
|
||||
- Nest with other RadioGroup or Checkboxes. If possible, keep all the options at the same level.
|
||||
|
||||
## Sample Code:
|
||||
```jsx
|
||||
<RadioGroup label="This is a test RadioGroup" defaultSelectedKey="A">
|
||||
<RadioButton content="Option A" buttonKey="A" />
|
||||
<RadioButton content="Option B" buttonKey="B" />
|
||||
<RadioButton content="Option C" buttonKey="C" disabled={true} />
|
||||
<RadioButton content="Option D" buttonKey="D" />
|
||||
</RadioGroup>
|
||||
```
|
||||
|
||||
## Tokens:
|
||||
RadioButton supports the following tokens:
|
||||
1. borderColor – This changes the border color of the RadioButton.
|
||||
2. backgroundColor – This changes the background color of the inner circle of the RadioButton.
|
||||
3. color – This changes the text color of the label associated with the RadioButton.
|
||||
|
||||
RadioGroup supports the following tokens:
|
||||
1. fontFamily - Changes the font family of the label associated with the RadioGroup.
|
||||
2. fontSize - Changes the font size of the label associated with the RadioGroup.
|
||||
3. fontWeight - Changes the font weight of the label associated with the RadioGroup.
|
||||
4. color - This changes the text color of the label associated with the RadioGroup.
|
|
@ -1,46 +0,0 @@
|
|||
# Separator
|
||||
|
||||
## Horizontal Example
|
||||
```
|
||||
import { ISeparator, Separator, Stack, Text } from '@fluentui/react-native';
|
||||
|
||||
const stackStyle: IStackProps['style'] = {
|
||||
borderWidth: 1,
|
||||
borderColor: '#bdbdbd',
|
||||
padding: 8,
|
||||
margin: 8
|
||||
};
|
||||
|
||||
export const SeparatorExample: React.FunctionComponent<{}> = props => {
|
||||
return (
|
||||
<Stack style={ stackStyle } gap={ 5 }>
|
||||
<Text>This is a text element</Text>
|
||||
<Separator />
|
||||
<Text>This is a longer text element</Text>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## Vertical Example
|
||||
```
|
||||
import { ISeparator, Separator, Stack } from '@fluentui/react-native';
|
||||
|
||||
const separatorStackStyle: IStackProps['style'] = {
|
||||
height: 200,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-evenly'
|
||||
};
|
||||
|
||||
export const SeparatorExample: React.FunctionComponent<{}> = props => {
|
||||
return (
|
||||
<Stack gap={ 4 } style={ separatorStackStyle }>
|
||||
<Text>Text 1</Text>
|
||||
<Separator color="blue" vertical />
|
||||
<Text>Text 2</Text>
|
||||
<Separator color="red" vertical />
|
||||
<Text>Text 3</Text>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
```
|
|
@ -1,3 +0,0 @@
|
|||
# Text
|
||||
|
||||
### Not Yet Implemented
|
|
@ -1,22 +0,0 @@
|
|||
- name: Components
|
||||
items:
|
||||
- name: Button
|
||||
link: windows/components/button
|
||||
- name: Checkbox
|
||||
link: windows/components/checkbox
|
||||
- name: Link
|
||||
link: windows/components/link
|
||||
- name: RadioGroup
|
||||
link: windows/components/radiogroup
|
||||
- name: Separator
|
||||
link: windows/components/separator
|
||||
- name: Text
|
||||
link: windows/components/text
|
||||
- name: Utilities
|
||||
items:
|
||||
- name: FocusTrapZone
|
||||
link: windows/components/utilities/focustrapzone
|
||||
- name: Pressable
|
||||
link: windows/components/utilities/pressable
|
||||
- name: Stack
|
||||
link: windows/components/utilities/stack
|
|
@ -1,24 +0,0 @@
|
|||
# FocusTrapZone Documentation
|
||||
|
||||
## Example
|
||||
|
||||
```
|
||||
import { IFocusTrapZoneProps, FocusTrapZone, Button } from '@fluentui/react-native';
|
||||
|
||||
const activeTrapZoneStyle: IFocusTrapZoneProps['style'] = {
|
||||
borderWidth: 2,
|
||||
borderColor: '#ababab',
|
||||
borderStyle: 'solid',
|
||||
padding: 10
|
||||
}
|
||||
|
||||
export const FocusTrapZoneExample: React.FunctionComponent<{}> = props => {
|
||||
return (
|
||||
<FocusTrapZone style={activeTrapZoneStyle}>
|
||||
<Button content="Button 1" />
|
||||
<Button content="Button 2" />
|
||||
<Button content="Button 3" />
|
||||
</FocusTrapZone>
|
||||
);
|
||||
};
|
||||
```
|
|
@ -1,3 +0,0 @@
|
|||
# Pressable
|
||||
|
||||
### Not Yet Implemented
|
|
@ -1,3 +0,0 @@
|
|||
# Stack
|
||||
|
||||
### Not Yet Implemented
|
|
@ -1,15 +0,0 @@
|
|||
import React, { useState } from 'react';
|
||||
|
||||
export const Counter = function() {
|
||||
// Declare a new state variable, which we'll call "count"
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>You clicked {count} times</p>
|
||||
<button onClick={() => setCount(count + 1)}>Click me</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Counter;
|
|
@ -1,3 +0,0 @@
|
|||
---
|
||||
title: Experiences
|
||||
---
|
|
@ -1,2 +0,0 @@
|
|||
- name: Example
|
||||
link: windows
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
title: Get started
|
||||
---
|
||||
|
||||
#
|
|
@ -1,2 +0,0 @@
|
|||
- name: Example
|
||||
link: windows
|
|
@ -1,50 +0,0 @@
|
|||
import Counter from './counter.jsx';
|
||||
|
||||
# JDX in MDX
|
||||
|
||||
You can import React components in an `.mdx` file. Like this:
|
||||
|
||||
> ```jsx
|
||||
> import Counter from './counter.jsx';
|
||||
> <Counter />;
|
||||
> ```
|
||||
|
||||
<Counter />
|
||||
|
||||
# FluentUI Docs
|
||||
|
||||
## Components
|
||||
|
||||
- [Button](/windows/components/button)
|
||||
- [Link](/windows/components/link)
|
||||
- [Separator](/windows/components/separator)
|
||||
- [Text](/windows/components/text)
|
||||
|
||||
## Utilities
|
||||
|
||||
- [FocusTrapZone](/windows/components/utilities/focustrapzone)
|
||||
- [Pressable](/windows/components/utilities/pressable)
|
||||
- [Stack](/windows/components/utilities/stack)
|
||||
|
||||
## Contributing Docs
|
||||
|
||||
- Follow and maintain the template below
|
||||
- Run the local dev server with:
|
||||
> ```
|
||||
> cd fluentui-react-native
|
||||
> yarn
|
||||
> cd Docs
|
||||
> yarn dev
|
||||
> ```
|
||||
|
||||
## Template
|
||||
|
||||
> ```
|
||||
> # Component Name
|
||||
>
|
||||
> ## Purpose
|
||||
>
|
||||
> ## Do's and Don'ts
|
||||
>
|
||||
> ## Sample Code
|
||||
> ```
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
title: Styles
|
||||
---
|
||||
|
||||
styles
|
|
@ -1,2 +0,0 @@
|
|||
- name: Example
|
||||
link: windows
|
|
@ -1,16 +0,0 @@
|
|||
module.exports = {
|
||||
siteMetadata: {
|
||||
siteURL: 'https://fluentui.z5.web.core.windows.net/',
|
||||
},
|
||||
plugins: [
|
||||
`gatsby-plugin-typescript`,
|
||||
`gatsby-plugin-sharp`,
|
||||
'gatsby-transformer-sharp',
|
||||
{
|
||||
resolve: `gatsby-theme-fluent-site`,
|
||||
options: {
|
||||
contentPath: `./content`,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
|
@ -1,72 +0,0 @@
|
|||
{
|
||||
"name": "fluent-rn-website",
|
||||
"version": "0.3.5",
|
||||
"description": "Fluent website content",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/microsoft/fluent-site"
|
||||
},
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"clean": "gatsby clean",
|
||||
"build": "gatsby build --prefix-paths",
|
||||
"develop": "gatsby clean && gatsby develop --port 3000",
|
||||
"serve": "gatsby serve",
|
||||
"start": "npm run develop"
|
||||
},
|
||||
"devDependencies": {
|
||||
"gatsby-theme-fluent-site": "^0.2.0",
|
||||
"gatsby-plugin-sharp": "^2.3.13",
|
||||
"gatsby-transformer-sharp": "^2.3.12"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.8.3",
|
||||
"@mdx-js/mdx": "^1.1.0",
|
||||
"@mdx-js/react": "^1.0.27",
|
||||
"@storybook/addon-actions": "^5.3.4",
|
||||
"@storybook/addon-links": "^5.3.4",
|
||||
"@storybook/addons": "^5.3.4",
|
||||
"@storybook/react": "^5.3.4",
|
||||
"@types/graphql": "^14.2.0",
|
||||
"@types/node": "^11.13.13",
|
||||
"@types/react": "^16.8.19",
|
||||
"@types/react-dom": "^16.8.4",
|
||||
"@types/react-helmet": "^5.0.8",
|
||||
"babel-loader": "^8.0.6",
|
||||
"gatsby-image": "^2.2.38",
|
||||
"gatsby-link": "^2.2.2",
|
||||
"gatsby-plugin-docs-creator": "^2.2.0",
|
||||
"gatsby-plugin-manifest": "^2.2.20",
|
||||
"gatsby-plugin-mdx": "^1.0.67",
|
||||
"gatsby-plugin-netlify-cms": "^4.2.2",
|
||||
"gatsby-plugin-offline": "^3.0.32",
|
||||
"gatsby-plugin-react-helmet": "^3.1.21",
|
||||
"gatsby-plugin-sharp": "^2.3.13",
|
||||
"gatsby-remark-copy-linked-files": "^2.1.36",
|
||||
"gatsby-remark-images": "^3.1.42",
|
||||
"gatsby-remark-prismjs": "^3.3.30",
|
||||
"gatsby-source-filesystem": "^2.1.46",
|
||||
"gatsby-source-git": "^1.0.2",
|
||||
"gatsby-transformer-remark": "^2.6.48",
|
||||
"gatsby-transformer-sharp": "^2.3.12",
|
||||
"gatsby-transformer-yaml": "^2.2.24",
|
||||
"monaco-editor-webpack-plugin": "^1.9.0",
|
||||
"netlify-cms-app": "^2.12.2",
|
||||
"office-ui-fabric-react": "^7.92.0",
|
||||
"prism-react-renderer": "^1.0.1",
|
||||
"react-helmet": "^5.2.1",
|
||||
"react-live": "^2.1.2",
|
||||
"react-monaco-editor": "^0.34.0",
|
||||
"remark-parse": "^7.0.0",
|
||||
"remark-react": "^6.0.0",
|
||||
"unified": "^8.3.2",
|
||||
"gatsby-plugin-typescript": "^2.1.26",
|
||||
"typescript": "^3.5.1",
|
||||
"gatsby": "^2.19.27",
|
||||
"gatsby-plugin-emotion": "^4.1.23",
|
||||
"react": "^16.13.0",
|
||||
"react-dom": "^16.13.0",
|
||||
"@fluentui/react-native": "^0.15.5",
|
||||
"@mdx-js/loader": "^1.5.5"
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
# Hello World Fluent
|
||||
|
||||
Now that you have the FluentUI React Native package installed, we will add components from the library to a new React Native project.
|
||||
|
||||
```
|
||||
// In App.js in a new project
|
||||
|
||||
import React from 'react';
|
||||
import { View, Text } from 'react-native';
|
||||
import { Checkbox } from '@fluentui/react-native';
|
||||
|
||||
function HelloWorldApp() {
|
||||
return (
|
||||
<View
|
||||
style={{ }}
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}>
|
||||
<Text>Hello, world!</Text>
|
||||
<Checkbox label="Hello World Checkbox"/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
export default HelloWorldApp;
|
||||
```
|
|
@ -1,204 +0,0 @@
|
|||
# Accessibility
|
||||
|
||||
Accessibility means designing and creating UI that's accessible to all users, including those with disabilities.
|
||||
It's important to create accessible UI so that assitive technology, like screen readers, can access information users
|
||||
need to interact with your controls. Here you can find a guide to get started as well as tools to help you test your UI
|
||||
for accessibility.
|
||||
|
||||
## Our Approach
|
||||
FluentUI React Native enables developers to create accessible UI by providing APIs that are complimentary to Microsoft
|
||||
UI Automation (UIA), an accessibility framework for Windows. Our API surface is heavily inspired by Accessible Internet Rich
|
||||
Applications (ARIA), an accessibility framework for the web. We chose to align with ARIA to provide a "web first"
|
||||
development experience. ARIA also provides a lot of guidelines and best practices for creating accessible content.
|
||||
|
||||
## Making UI accessible
|
||||
|
||||
FluentUI React Native components are accessible by default meaning you only need to provide minimal customizations to make
|
||||
your UI accessible. Here you will find information on the folowing components: Properties, Roles, States, and Actions.
|
||||
|
||||
### Properties
|
||||
|
||||
#### accessible
|
||||
When `true`, indicates that the view is an accessible element meaning it receives accessibility focus. By default, all
|
||||
controls are accessible.
|
||||
|
||||
```tsx
|
||||
<Text accessible={true}>first</Text>
|
||||
<Text accessible={false}>second</Text>
|
||||
```
|
||||
|
||||
In the above example, 'first' recieves accessiblity focus but 'second' does not.
|
||||
|
||||
|
||||
#### accessibilityLabel
|
||||
When something is accessible, it's good practice to set an `accessiblityLabel` so the user knows what element has focus. Narrator will read out this string when the user places focus on the control.
|
||||
|
||||
To use, set the `accessibilityLabel` property to a custom string:
|
||||
|
||||
```tsx
|
||||
<Button content="Click me" accessibilityLabel="Press me" />
|
||||
```
|
||||
|
||||
In the above example, Narrator will read "Press me" instead of "Click me".
|
||||
|
||||
#### accessibilityHint
|
||||
|
||||
An accessibility hint helps users understand what will happen when they preform an action on the accessibility element when
|
||||
the result isn't clear from the accessibility label.
|
||||
|
||||
To use, set the `accessibilityHint` property to a custom string:
|
||||
|
||||
```tsx
|
||||
<Button content="Back" accessibilityLabel="Go Back" accessibilityHint="Navigates to the previous screen" />
|
||||
```
|
||||
|
||||
In the above example, Narrator will read the hint after the label.
|
||||
|
||||
#### acceptsKeyboardFocus
|
||||
|
||||
When `true`, this enables the component to accept keyboard focus.
|
||||
|
||||
#### accessibilityLevel, accessibiiltyPositionInSet, accessibilitySetSize
|
||||
|
||||
These properties are used together to define a set of items.
|
||||
- ```accessibilityLevel``` defines the level of the set
|
||||
- ```accessibilityPositionInSet``` defines the position of the item in a set
|
||||
- ```accessibilitySetSize``` defines the number of items in a set
|
||||
|
||||
```tsx
|
||||
<View>
|
||||
<Button content="One" accessibilityLevel={1} accessibiltyPositionInSet={1} accessibilitySetSize={2}/>
|
||||
<Button content="Two" accessibilityLevel={1} accessibiltyPositionInSet={2} accessibilitySetSize={2}/>
|
||||
<View>
|
||||
<Button content="Three" accessibilityLevel={2} accessibiltyPositionInSet={1} accessibilitySetSize={3} />
|
||||
<Button content="Four" accessibilityLevel={2} accessibiltyPositionInSet={2} accessibilitySetSize={3} />
|
||||
<Button content="Five" accessibilityLevel={2} accessibiltyPositionInSet={3} accessibilitySetSize={3} />
|
||||
</View>
|
||||
</View>
|
||||
```
|
||||
|
||||
In the above example, Narrator will read the first button as "One, 1 of 2, level 1" and the second button as "Two, 2 of
|
||||
2, level 1" and so on.
|
||||
|
||||
### Roles
|
||||
`accessibilityRole` communicates the purpose of a component to the user.
|
||||
|
||||
`accessibilityRole` can be one of the following:
|
||||
|
||||
- **adjustable** Used when an element can be "adjusted" (e.g. a slider).
|
||||
- **alert** Used when an element contains important text to be presented to the user. (note: maps to UIA Group control type)
|
||||
- **alertdialog** Used to represent a dialog that contains an alert message. (note: maps to UIA Pane control type)
|
||||
- **application** Used to represent a structure containing one or more focusable elements requiring user input such as keyboard or gesture events. (note: maps to UIA Pane control type)
|
||||
- **button** Used when the element should be treated as a button.
|
||||
- **checkbox** Used when an element represents a checkbox which can be checked, unchecked, or have mixed checked state.
|
||||
- **combobox** Used when an element represents a combo box, which allows the user to select among several choices.
|
||||
- **dialog** Used to represent a descendant window of a primary window of an application. (note: maps to UIA Pane control type)
|
||||
- **group** Used to represent a set of user interface objects.
|
||||
- **header** Used when an element acts as a header for a content section (e.g. the title of a navigation bar) (note: maps to UIA Text control type)
|
||||
- **image** Used when the element should be treated as an image.
|
||||
- **imagebutton** Used when the element should be treated as a button and is also an image. (note: maps to UIA Button control type)
|
||||
- **keyboardkey** Used when the element acts as a keyboard key. (note: maps to UIA Button control type)
|
||||
- **link** Used when the element should be treated as a link.
|
||||
- **menu** Used when the component is a menu of choices.
|
||||
- **menubar** Used when a component is a container of multiple menus.
|
||||
- **menuitem** Used to represent an item within a menu.
|
||||
- **none** Used when the element has no role. (note: maps to UIA Group control type and is removed from the control tree)
|
||||
- **progressbar** Used to represent a component which indicates progress of a task.
|
||||
- **presentation** Used to represent an element whose implicit native role semantics will not be mapped to the accessibility API. Synonymn is none. (note: maps to UIA Group control type and is removed from the control tree)
|
||||
- **radio** Used to represent a radio button.
|
||||
- **radiogroup** Used to represent a group of radio buttons. (note: maps to UIA List control type)
|
||||
- **scrollbar** Used to represent a scroll bar.
|
||||
- **search** Used when the text field element should also be treated as a search field (note: maps to UIA Group control type)
|
||||
- **spinbutton** Used to represent a button which opens a list of choices. (note: maps to UIA Spinner control type)
|
||||
- **summary** Used when an element can be used to provide a quick summary of current conditions in the app when the app first launches. (note: maps to UIA Text control type)
|
||||
- **switch** Used to represent a switch which can be turned on and off. (note: maps to UIA Button control type)
|
||||
- **tab** Used to represent a tab. (note: maps to UIA Tabitem control type)
|
||||
- **tablist** Used to represent a list of tabs. (note: maps to UIA Tab control type)
|
||||
- **tabpanel** Used to represent a container for the resources associated with a tab, where each tab is contained in a tablist. (note: maps to UIA Pane contorl type)
|
||||
- **text** Used when the element should be treated as static text that cannot change.
|
||||
- **textbox** Used whent the element should be treated as a type of input that allows free-form text as its value. (note: maps to UIA Edit control type)
|
||||
- **timer** Used to represent a timer. (note: maps to UIA Group control type)
|
||||
- **toolbar** Used to represent a tool bar (a container of action buttons or components).
|
||||
- **tree** Used to represent a list that contains sub-level nested groups that can be collapsed and expanded.
|
||||
- **treeitem** Used to represent an option item in a tree that may be expanded or collapsed if it contains a sub-level group of tree item elements.
|
||||
|
||||
### States
|
||||
|
||||
`accessibilityStates` is an array of values, and may include any of the following:
|
||||
|
||||
- **disabled** Used when the element is disabled and cannot be interacted with.
|
||||
- **checked** Used to indicate that a checkable element is currently checked.
|
||||
- **unchecked** Used to indicate that a checkable element is not currently checked.
|
||||
- **mixed** Used to indicate that a checkable element is in a mixed state.
|
||||
- **expanded** Used to indicate that an expandable element is currently expanded.
|
||||
- **selected** Used when the element is in a selected state.
|
||||
- **required** Used to indicate that selection is required for a group of elements.
|
||||
- **multiselectable** Used to indicate that multiple items can be selected from a group.
|
||||
|
||||
To use, set the `accessibilityStates` to an array containing the list of current states.
|
||||
|
||||
### Actions
|
||||
|
||||
Accessibility actions allow an assistive technology to programmatically invoke the actions of a component. In order to support accessibility actions, a component must do two things:
|
||||
|
||||
- Define the list of actions it supports via the `accessibilityActions` property.
|
||||
- Implement an `onAccessibilityAction` function to handle action requests.
|
||||
|
||||
The `accessibilityActions` property should contain a list of action objects. Each action object should contain the following fields:
|
||||
|
||||
| Name | Type | Required |
|
||||
| ----- | ------ | -------- |
|
||||
| name | string | Yes |
|
||||
| label | string | No |
|
||||
|
||||
Actions either represent standard actions, such as clicking a button or adjusting a slider, or custom actions specific to a given component such as deleting an email message. The `name` field is required for both standard and custom actions, but `label` is optional for standard actions.
|
||||
|
||||
When adding support for standard actions, `name` must be one of the following:
|
||||
|
||||
- `'Expand'` - Displays all child nodes, controls, or content of the control.
|
||||
- `'Collapse'` - Hides all child nodes, controls, or content of this element.
|
||||
- `'Select'` - Deselects any selected items and then selects the current element.
|
||||
- `'AddToSelection'` - Adds the current element to the collection of selected items.
|
||||
- `'RemoveFromSelection'` - Removes the current element from the collection of selected items.
|
||||
- `'Toggle'` - Cycles through the toggle states of a control.
|
||||
|
||||
Note: These are the only standard actions we have support for at this time. When new actions are added, they will be updated here.
|
||||
|
||||
The `label` field is optional for standard actions, and is often unused by assistive technologies. For custom actions, it is a localized string containing a description of the action to be presented to the user.
|
||||
|
||||
To handle action requests, a component must implement an `onAccessibilityAction` function. The only argument to this function is an event containing the name of the action to perform. The below example shows how to use standard actions.
|
||||
|
||||
```tsx
|
||||
<View
|
||||
accessible={true}
|
||||
accessibilityActions={[
|
||||
{name: 'Expand'},
|
||||
{name: 'Collapse'},
|
||||
]}
|
||||
onAccessibilityAction={(event) => {
|
||||
switch (event.nativeEvent.actionName) {
|
||||
case 'Expand':
|
||||
Alert.alert('Alert', 'Expand action success');
|
||||
break;
|
||||
case 'Collapse':
|
||||
Alert.alert('Alert', 'Collapse action success');
|
||||
break;
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
To test your application with Narrator, visit the complete guide to Narrator: https://support.microsoft.com/en-us/help/22798/windows-10-complete-guide-to-narrator
|
||||
|
||||
To test your application wiht Accessibility Insights, visit Accessibility Insights for Windows: https://accessibilityinsights.io/docs/en/windows/overview
|
||||
|
||||
## Checklist
|
||||
- Is the control marked as accessible?
|
||||
- Does the control acceptKeyboardFocus?
|
||||
- Does the control have the correct accessibilityRole set?
|
||||
- Does the control have the correct acccessibilityStates set?
|
||||
- Does the control have the correct accessibilityActions set?
|
||||
- Does the control include appropriate customizations (accessibilityLabel, accessibilityHint, etc.)?
|
||||
- Try testing it with Narrator. Does narrator annouce the control correctly? When interacting with it, does it have the correct behavior?
|
||||
- Try testing it with AccessibilityInsights. Does the control expose the correct UIA patterns? Does the control reflect the right properties, role, states and actions?
|
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"include": ["./src/**/*"],
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "commonjs",
|
||||
"lib": ["dom", "es2017"],
|
||||
// "allowJs": true,
|
||||
// "checkJs": true,
|
||||
"jsx": "react",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"noEmit": true,
|
||||
"skipLibCheck": true,
|
||||
"noImplicitAny": false
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directory
|
||||
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
|
||||
node_modules
|
||||
|
||||
decls
|
||||
dist
|
|
@ -1,34 +0,0 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directory
|
||||
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
|
||||
node_modules
|
||||
*.un~
|
||||
yarn.lock
|
||||
src
|
||||
flow-typed
|
||||
coverage
|
||||
decls
|
||||
examples
|
|
@ -1,50 +0,0 @@
|
|||
{
|
||||
"name": "gatsby-plugin-docs-creator",
|
||||
"entries": [
|
||||
{
|
||||
"date": "Fri, 17 Apr 2020 22:36:03 GMT",
|
||||
"tag": "gatsby-plugin-docs-creator_v2.2.0",
|
||||
"version": "2.2.0",
|
||||
"comments": {
|
||||
"minor": [
|
||||
{
|
||||
"comment": "yarn.lock",
|
||||
"author": "warleu@microsoft.com",
|
||||
"commit": "205e7b7e88151a17f2560ecdaec1c6e13c5825b7",
|
||||
"package": "gatsby-plugin-docs-creator"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Thu, 09 Apr 2020 18:39:15 GMT",
|
||||
"tag": "gatsby-plugin-docs-creator_v2.1.42",
|
||||
"version": "2.1.42",
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"comment": "merge conflicts",
|
||||
"author": "ppatboyd@outlook.com",
|
||||
"commit": "232a97794dc044cec9671e5cca4cf204d881f614",
|
||||
"package": "gatsby-plugin-docs-creator"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Wed, 08 Apr 2020 21:26:12 GMT",
|
||||
"tag": "gatsby-plugin-docs-creator_v2.1.41",
|
||||
"version": "2.1.41",
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"comment": "update package name",
|
||||
"author": "mgodbolt@microsoft.com",
|
||||
"commit": "232a97794dc044cec9671e5cca4cf204d881f614",
|
||||
"package": "gatsby-plugin-docs-creator"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
# Change Log - gatsby-plugin-docs-creator
|
||||
|
||||
This log was last generated on Fri, 17 Apr 2020 22:36:03 GMT and should not be manually modified.
|
||||
|
||||
<!-- Start content -->
|
||||
|
||||
## 2.2.0
|
||||
|
||||
Fri, 17 Apr 2020 22:36:03 GMT
|
||||
|
||||
### Minor changes
|
||||
|
||||
- yarn.lock (warleu@microsoft.com)
|
||||
|
||||
## 2.1.42
|
||||
Thu, 09 Apr 2020 18:39:15 GMT
|
||||
|
||||
### Patches
|
||||
|
||||
- merge conflicts (ppatboyd@outlook.com)
|
||||
## 2.1.41
|
||||
Wed, 08 Apr 2020 21:26:12 GMT
|
||||
|
||||
### Patches
|
||||
|
||||
- update package name (mgodbolt@microsoft.com)
|
|
@ -1,104 +0,0 @@
|
|||
# gatsby-plugin-docs-creator
|
||||
|
||||
Gatsby plugin that automatically creates pages from React components in specified directories. Gatsby
|
||||
includes this plugin automatically in all sites for creating pages from components in `src/pages`.
|
||||
|
||||
You may include another instance of this plugin if you'd like to create additional "pages" directories.
|
||||
|
||||
With this plugin, _any_ file that lives in the specified pages folder (e.g. the default `src/pages`) or subfolders will be expected to export a React Component to generate a Page. The following files are automatically excluded:
|
||||
|
||||
- `template-*`
|
||||
- `__tests__/*`
|
||||
- `*.test.jsx?`
|
||||
- `*.spec.jsx?`
|
||||
- `*.d.tsx?`
|
||||
- `*.json`
|
||||
- `*.yaml`
|
||||
- `_*`
|
||||
- `.*`
|
||||
|
||||
To exclude custom patterns, see [Ignoring Specific Files](#ignoring-specific-files)
|
||||
|
||||
## Install
|
||||
|
||||
`npm install --save gatsby-plugin-docs-creator`
|
||||
|
||||
## How to use
|
||||
|
||||
```javascript
|
||||
// gatsby-config.js
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
// You can have multiple instances of this plugin
|
||||
// to create pages from React components in different directories.
|
||||
//
|
||||
// The following sets up the pattern of having multiple
|
||||
// "pages" directories in your project
|
||||
{
|
||||
resolve: `gatsby-plugin-docs-creator`,
|
||||
options: {
|
||||
path: `${__dirname}/src/account/pages`,
|
||||
},
|
||||
},
|
||||
{
|
||||
resolve: `gatsby-plugin-docs-creator`,
|
||||
options: {
|
||||
path: `${__dirname}/src/settings/pages`,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
### Ignoring Specific Files
|
||||
|
||||
#### Shorthand
|
||||
|
||||
```javascript
|
||||
// The following example will disable the `/blog` index page
|
||||
|
||||
// gatsby-config.js
|
||||
module.exports = {
|
||||
plugins: [
|
||||
{
|
||||
resolve: `gatsby-plugin-docs-creator`,
|
||||
options: {
|
||||
path: `${__dirname}/src/indexes/pages`,
|
||||
ignore: [`blog.(js|ts)?(x)`],
|
||||
// See pattern syntax recognized by micromatch
|
||||
// https://www.npmjs.com/package/micromatch#matching-features
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
**NOTE**: The above code snippet will only stop the creation of the `/blog` page, which is defined as a React component.
|
||||
This plugin does not affect programmatically generated pages from the [createPagesAPI](https://www.gatsbyjs.org/docs/node-apis/#createPages).
|
||||
|
||||
#### Ignore Options
|
||||
|
||||
```javascript
|
||||
// The following example will ignore pages using case-insensitive matching
|
||||
|
||||
// gatsby-config.js
|
||||
module.exports = {
|
||||
plugins: [
|
||||
{
|
||||
resolve: `gatsby-plugin-docs-creator`,
|
||||
options: {
|
||||
path: `${__dirname}/src/examples/pages`,
|
||||
ignore: {
|
||||
// Example: Ignore `file.example.js`, `dir/s/file.example.tsx`
|
||||
patterns: [`**/*.example.(js|ts)?(x)`],
|
||||
// Example: Match both `file.example.js` and `file.EXAMPLE.js`
|
||||
options: { nocase: true },
|
||||
// See all available micromatch options
|
||||
// https://www.npmjs.com/package/micromatch#optionsnocase
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
|
@ -1,9 +0,0 @@
|
|||
export declare const createPagesStatefully: ({ store, actions, reporter }: {
|
||||
store: any;
|
||||
actions: any;
|
||||
reporter: any;
|
||||
}, { path: pagesPath, pathCheck, ignore }: {
|
||||
path: any;
|
||||
pathCheck?: boolean | undefined;
|
||||
ignore: any;
|
||||
}, doneCb: any) => Promise<void>;
|
|
@ -1,149 +0,0 @@
|
|||
'use strict';
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
var tslib_1 = require('tslib');
|
||||
var lodash_1 = tslib_1.__importDefault(require('lodash'));
|
||||
var js_yaml_1 = tslib_1.__importDefault(require('js-yaml'));
|
||||
var gatsby_page_utils_1 = require('gatsby-page-utils');
|
||||
var BBPromise = require('bluebird');
|
||||
var existsSync = require('fs-exists-cached').sync;
|
||||
var systemPath = require('path');
|
||||
var readFileSync = require('fs').readFileSync;
|
||||
var globCB = require('glob');
|
||||
var glob = BBPromise.promisify(globCB);
|
||||
// Path creator.
|
||||
// Auto-create pages.
|
||||
// algorithm is glob /pages directory for js/jsx/cjsx files *not*
|
||||
// underscored. Then create url w/ our path algorithm *unless* user
|
||||
// takes control of that page component in gatsby-node.
|
||||
exports.createPagesStatefully = function(_a, _b, doneCb) {
|
||||
var store = _a.store,
|
||||
actions = _a.actions,
|
||||
reporter = _a.reporter;
|
||||
var pagesPath = _b.path,
|
||||
_c = _b.pathCheck,
|
||||
pathCheck = _c === void 0 ? true : _c,
|
||||
ignore = _b.ignore;
|
||||
return tslib_1.__awaiter(void 0, void 0, void 0, function() {
|
||||
var createPage, deletePage, program, exts, findNearestFile, pagesDirectory, pagesGlob, tocGlob, files, tocs;
|
||||
return tslib_1.__generator(this, function(_d) {
|
||||
switch (_d.label) {
|
||||
case 0:
|
||||
(createPage = actions.createPage), (deletePage = actions.deletePage);
|
||||
program = store.getState().program;
|
||||
exts = program.extensions
|
||||
.map(function(e) {
|
||||
return '' + e.slice(1);
|
||||
})
|
||||
.join(',');
|
||||
if (!pagesPath) {
|
||||
reporter.panic(
|
||||
'\n "path" is a required option for gatsby-plugin-page-creator\n\n See docs here - https://www.gatsbyjs.org/plugins/gatsby-plugin-page-creator/\n '
|
||||
);
|
||||
}
|
||||
// Validate that the path exists.
|
||||
if (pathCheck && !existsSync(pagesPath)) {
|
||||
reporter.panic(
|
||||
'\n The path passed to gatsby-plugin-page-creator does not exist on your file system:\n\n ' +
|
||||
pagesPath +
|
||||
'\n\n Please pick a path to an existing directory.\n '
|
||||
);
|
||||
}
|
||||
findNearestFile = function(matchPath, filePaths) {
|
||||
if (filePaths === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
var matchParts = matchPath.split('/');
|
||||
do {
|
||||
var match = filePaths.find(function(filePath) {
|
||||
var fileRelPath = systemPath.dirname(filePath);
|
||||
var matchRelPath = systemPath.dirname(matchParts.join('/'));
|
||||
return fileRelPath === matchRelPath;
|
||||
});
|
||||
if (match !== undefined) {
|
||||
return match;
|
||||
} else {
|
||||
matchParts.splice(-1, 1);
|
||||
}
|
||||
} while (matchParts.length > -1);
|
||||
return undefined;
|
||||
};
|
||||
pagesDirectory = systemPath.resolve(process.cwd(), pagesPath);
|
||||
pagesGlob = '**/*.{' + exts + '}';
|
||||
tocGlob = '**/toc.yml';
|
||||
return [4 /*yield*/, glob(pagesGlob, { cwd: pagesPath })];
|
||||
case 1:
|
||||
files = _d.sent();
|
||||
return [4 /*yield*/, glob(tocGlob, { cwd: pagesPath })];
|
||||
case 2:
|
||||
tocs = _d.sent();
|
||||
files.forEach(function(file) {
|
||||
var tocPath = findNearestFile(file, tocs);
|
||||
_createPage(file, pagesDirectory, createPage, ignore, tocPath);
|
||||
});
|
||||
gatsby_page_utils_1
|
||||
.watchDirectory(
|
||||
pagesPath,
|
||||
pagesGlob,
|
||||
function(addedPath) {
|
||||
if (!lodash_1.default.includes(files, addedPath)) {
|
||||
var tocPath = findNearestFile(addedPath, tocs);
|
||||
_createPage(addedPath, pagesDirectory, createPage, ignore, tocPath);
|
||||
files.push(addedPath);
|
||||
}
|
||||
},
|
||||
function(removedPath) {
|
||||
// Delete the page for the now deleted component.
|
||||
var componentPath = systemPath.join(pagesDirectory, removedPath);
|
||||
store.getState().pages.forEach(function(page) {
|
||||
if (page.component === componentPath) {
|
||||
deletePage({
|
||||
path: gatsby_page_utils_1.createPath(removedPath),
|
||||
component: componentPath
|
||||
});
|
||||
}
|
||||
});
|
||||
files = files.filter(function(f) {
|
||||
return f !== removedPath;
|
||||
});
|
||||
}
|
||||
)
|
||||
.then(function() {
|
||||
return doneCb();
|
||||
});
|
||||
return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
var _createPage = function(filePath, pagesDirectory, createPage, ignore, tocPath) {
|
||||
// Filter out special components that shouldn't be made into
|
||||
// pages.
|
||||
if (!gatsby_page_utils_1.validatePath(filePath)) {
|
||||
return;
|
||||
}
|
||||
// Filter out anything matching the given ignore patterns and options
|
||||
if (gatsby_page_utils_1.ignorePath(filePath, ignore)) {
|
||||
return;
|
||||
}
|
||||
var toc = undefined;
|
||||
if (tocPath !== undefined) {
|
||||
try {
|
||||
toc = js_yaml_1.default.safeLoad(readFileSync(systemPath.join(pagesDirectory, tocPath), 'utf8'));
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
// Create page object
|
||||
var createdPath = gatsby_page_utils_1.createPath(filePath);
|
||||
var page = {
|
||||
path: createdPath,
|
||||
component: systemPath.join(pagesDirectory, filePath),
|
||||
context: {
|
||||
toc: toc,
|
||||
rootPath: filePath.substring(0, filePath.indexOf('/'))
|
||||
}
|
||||
};
|
||||
// Add page
|
||||
createPage(page);
|
||||
};
|
||||
//# sourceMappingURL=gatsby-node.js.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"gatsby-node.js","sourceRoot":"","sources":["../src/gatsby-node.ts"],"names":[],"mappings":";;;AAAA,0DAAuB;AACvB,4DAA2B;AAC3B,uDAAyF;AAEzF,IAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AACtC,IAAM,UAAU,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC;AACpD,IAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAC3B,IAAA,yCAAY,CAAmB;AACvC,IAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAC/B,IAAM,IAAI,GAAG,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;AAEzC,gBAAgB;AAChB,qBAAqB;AACrB,iEAAiE;AACjE,mEAAmE;AACnE,uDAAuD;AAC1C,QAAA,qBAAqB,GAAG,UAAO,EAA4B,EAAE,EAA6C,EAAE,MAAM;QAAjF,gBAAK,EAAE,oBAAO,EAAE,sBAAQ;QAAM,mBAAe,EAAE,iBAAgB,EAAhB,qCAAgB,EAAE,kBAAM;;;;;;oBAC3G,UAAU,GAAiB,OAAO,WAAxB,EAAE,UAAU,GAAK,OAAO,WAAZ,CAAa;oBACrC,OAAO,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC;oBACnC,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,UAAA,CAAC,IAAI,OAAA,KAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAG,EAAf,CAAe,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;oBAEpE,IAAI,CAAC,SAAS,EAAE;wBACd,QAAQ,CAAC,KAAK,CACZ,oKAIC,CACF,CAAC;qBACH;oBAED,iCAAiC;oBACjC,IAAI,SAAS,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE;wBACvC,QAAQ,CAAC,KAAK,CACZ,wGAGE,SAAS,mEAGV,CACF,CAAC;qBACH;oBAEK,eAAe,GAAG,UAAC,SAAiB,EAAE,SAAmB;wBAC7D,IAAI,SAAS,KAAK,SAAS,EAAE;4BAC3B,OAAO,SAAS,CAAC;yBAClB;wBACD,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;wBAClC,IAAM,UAAU,GAAG,SAAS,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;wBACnD,GAAG;4BACD,IAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,UAAA,QAAQ;gCACnC,IAAM,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;gCACjD,IAAM,YAAY,GAAG,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC;gCAC/E,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;gCACvC,OAAO,WAAW,KAAK,YAAY,CAAC;4BACtC,CAAC,CAAC,CAAC;4BACH,IAAI,KAAK,KAAK,SAAS,EAAE;gCACvB,OAAO,KAAK,CAAC;6BACd;;gCAAM,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;yBACjC,QAAQ,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE;wBAEjC,OAAO,SAAS,CAAC;oBACnB,CAAC,CAAC;oBAEI,cAAc,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC;oBAC9D,SAAS,GAAG,WAAS,IAAI,MAAG,CAAC;oBAC7B,OAAO,GAAG,YAAY,CAAC;oBAGjB,qBAAM,IAAI,CAAC,SAAS,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,EAAA;;oBAAjD,KAAK,GAAG,SAAyC;oBACxC,qBAAM,IAAI,CAAC,OAAO,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,EAAA;;oBAA9C,IAAI,GAAG,SAAuC;oBAEpD,KAAK,CAAC,OAAO,CAAC,UAAA,IAAI;wBAChB,IAAM,OAAO,GAAG,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;wBAC5C,WAAW,CAAC,IAAI,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;oBACjE,CAAC,CAAC,CAAC;oBAEH,kCAAc,CACZ,SAAS,EACT,SAAS,EACT,UAAA,SAAS;wBACP,IAAI,CAAC,gBAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE;4BACjC,IAAM,OAAO,GAAG,eAAe,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;4BACjD,WAAW,CAAC,SAAS,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;4BACpE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;yBACvB;oBACH,CAAC,EACD,UAAA,WAAW;wBACT,iDAAiD;wBACjD,IAAM,aAAa,GAAG,UAAU,CAAC,IAAI,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;wBACnE,KAAK,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,UAAA,IAAI;4BACjC,IAAI,IAAI,CAAC,SAAS,KAAK,aAAa,EAAE;gCACpC,UAAU,CAAC;oCACT,IAAI,EAAE,8BAAU,CAAC,WAAW,CAAC;oCAC7B,SAAS,EAAE,aAAa;iCACzB,CAAC,CAAC;6BACJ;wBACH,CAAC,CAAC,CAAC;wBACH,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,UAAA,CAAC,IAAI,OAAA,CAAC,KAAK,WAAW,EAAjB,CAAiB,CAAC,CAAC;oBAC/C,CAAC,CACF,CAAC,IAAI,CAAC,cAAM,OAAA,MAAM,EAAE,EAAR,CAAQ,CAAC,CAAC;;;;;CACxB,CAAC;AACF,IAAM,WAAW,GAAG,UAAC,QAAQ,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO;IACxE,4DAA4D;IAC5D,SAAS;IACT,IAAI,CAAC,gCAAY,CAAC,QAAQ,CAAC,EAAE;QAC3B,OAAO;KACR;IAED,qEAAqE;IACrE,IAAI,8BAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE;QAChC,OAAO;KACR;IAED,IAAI,GAAG,GAAG,SAAS,CAAC;IACpB,IAAI,OAAO,KAAK,SAAS,EAAE;QACzB,IAAI;YACF,GAAG,GAAG,iBAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,cAAc,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;SACrF;QAAC,OAAO,CAAC,EAAE;YACV,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;SAChB;KACF;IAED,qBAAqB;IACrB,IAAM,WAAW,GAAG,8BAAU,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAM,IAAI,GAAG;QACX,IAAI,EAAE,WAAW;QACjB,SAAS,EAAE,UAAU,CAAC,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC;QACpD,OAAO,EAAE;YACP,GAAG,EAAE,GAAG;YACR,QAAQ,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;SACvD;KACF,CAAC;IAEF,WAAW;IACX,UAAU,CAAC,IAAI,CAAC,CAAC;AACnB,CAAC,CAAC"}
|
|
@ -1 +0,0 @@
|
|||
module.exports = require('./dist/gatsby-node')
|
|
@ -1,50 +0,0 @@
|
|||
{
|
||||
"name": "gatsby-plugin-docs-creator",
|
||||
"version": "2.2.0",
|
||||
"description": "Gatsby plugin that automatically creates pages from React components in specified directories with additional docs related data",
|
||||
"main": "dist/gatsby-node.js",
|
||||
"scripts": {
|
||||
"build": "echo 'build'",
|
||||
"start": "yarn watch",
|
||||
"watch": "tsc -w --preserveWatchOutput",
|
||||
"prepare": "cross-env NODE_ENV=production npm run build"
|
||||
},
|
||||
"keywords": [
|
||||
"gatsby",
|
||||
"gatsby-plugin"
|
||||
],
|
||||
"author": "Micah Godbolt <mgodbolt@microsoft.com>",
|
||||
"contributors": [
|
||||
"Steven Natera <tektekpush@gmail.com> (https://twitter.com/stevennatera)",
|
||||
"Kyle Mathews <mathews.kyle@gmail.com>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/microsoft/fluent-site.git",
|
||||
"directory": "packages/gatsby-plugin-docs-creator"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/bluebird": "^3.5.29",
|
||||
"@types/js-yaml": "^3.12.2",
|
||||
"@types/lodash": "^4.14.149",
|
||||
"@types/node": "^13.7.4",
|
||||
"bluebird": "^3.7.2",
|
||||
"fs-exists-cached": "^1.0.0",
|
||||
"gatsby-page-utils": "^0.0.39",
|
||||
"glob": "^7.1.6",
|
||||
"js-yaml": "^3.13.1",
|
||||
"lodash": "^4.17.15",
|
||||
"micromatch": "^3.1.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cross-env": "^5.2.1",
|
||||
"tslib": "^1.10.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"gatsby": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
}
|
|
@ -1,136 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
import yaml from 'js-yaml';
|
||||
import { createPath, validatePath, ignorePath, watchDirectory } from 'gatsby-page-utils';
|
||||
|
||||
const BBPromise = require('bluebird');
|
||||
const existsSync = require(`fs-exists-cached`).sync;
|
||||
const systemPath = require(`path`);
|
||||
const { readFileSync } = require(`fs`);
|
||||
const globCB = require(`glob`);
|
||||
const glob = BBPromise.promisify(globCB);
|
||||
|
||||
// Path creator.
|
||||
// Auto-create pages.
|
||||
// algorithm is glob /pages directory for js/jsx/cjsx files *not*
|
||||
// underscored. Then create url w/ our path algorithm *unless* user
|
||||
// takes control of that page component in gatsby-node.
|
||||
export const createPagesStatefully = async ({ store, actions, reporter }, { path: pagesPath, pathCheck = true, ignore }, doneCb) => {
|
||||
const { createPage, deletePage } = actions;
|
||||
const program = store.getState().program;
|
||||
const exts = program.extensions.map(e => `${e.slice(1)}`).join(`,`);
|
||||
|
||||
if (!pagesPath) {
|
||||
reporter.panic(
|
||||
`
|
||||
"path" is a required option for gatsby-plugin-page-creator
|
||||
|
||||
See docs here - https://www.gatsbyjs.org/plugins/gatsby-plugin-page-creator/
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
// Validate that the path exists.
|
||||
if (pathCheck && !existsSync(pagesPath)) {
|
||||
reporter.panic(
|
||||
`
|
||||
The path passed to gatsby-plugin-page-creator does not exist on your file system:
|
||||
|
||||
${pagesPath}
|
||||
|
||||
Please pick a path to an existing directory.
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
const findNearestFile = (matchPath: string, filePaths: string[]): string | undefined => {
|
||||
if (filePaths === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
const matchParts = matchPath.split('/');
|
||||
do {
|
||||
const match = filePaths.find(filePath => {
|
||||
const fileRelPath = systemPath.dirname(filePath);
|
||||
const matchRelPath = systemPath.dirname(matchParts.join('/'));
|
||||
return fileRelPath === matchRelPath;
|
||||
});
|
||||
if (match !== undefined) {
|
||||
return match;
|
||||
} else matchParts.splice(-1, 1);
|
||||
} while (matchParts.length > 0);
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const pagesDirectory = systemPath.resolve(process.cwd(), pagesPath);
|
||||
const pagesGlob = `**/*.{${exts}}`;
|
||||
const tocGlob = '**/toc.yml';
|
||||
|
||||
// Get initial list of files.
|
||||
let files = await glob(pagesGlob, { cwd: pagesPath });
|
||||
const tocs = await glob(tocGlob, { cwd: pagesPath });
|
||||
|
||||
files.forEach(file => {
|
||||
const tocPath = findNearestFile(file, tocs);
|
||||
_createPage(file, pagesDirectory, createPage, ignore, tocPath);
|
||||
});
|
||||
|
||||
watchDirectory(
|
||||
pagesPath,
|
||||
pagesGlob,
|
||||
addedPath => {
|
||||
if (!_.includes(files, addedPath)) {
|
||||
const tocPath = findNearestFile(addedPath, tocs);
|
||||
_createPage(addedPath, pagesDirectory, createPage, ignore, tocPath);
|
||||
files.push(addedPath);
|
||||
}
|
||||
},
|
||||
removedPath => {
|
||||
// Delete the page for the now deleted component.
|
||||
const componentPath = systemPath.join(pagesDirectory, removedPath);
|
||||
store.getState().pages.forEach(page => {
|
||||
if (page.component === componentPath) {
|
||||
deletePage({
|
||||
path: createPath(removedPath),
|
||||
component: componentPath
|
||||
});
|
||||
}
|
||||
});
|
||||
files = files.filter(f => f !== removedPath);
|
||||
}
|
||||
).then(() => doneCb());
|
||||
};
|
||||
const _createPage = (filePath, pagesDirectory, createPage, ignore, tocPath) => {
|
||||
// Filter out special components that shouldn't be made into
|
||||
// pages.
|
||||
if (!validatePath(filePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Filter out anything matching the given ignore patterns and options
|
||||
if (ignorePath(filePath, ignore)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let toc = undefined;
|
||||
if (tocPath !== undefined) {
|
||||
try {
|
||||
toc = yaml.safeLoad(readFileSync(systemPath.join(pagesDirectory, tocPath), 'utf8'));
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Create page object
|
||||
const createdPath = createPath(filePath);
|
||||
const page = {
|
||||
path: createdPath,
|
||||
component: systemPath.join(pagesDirectory, filePath),
|
||||
context: {
|
||||
toc: toc,
|
||||
rootPath: filePath.substring(0, filePath.indexOf('/'))
|
||||
}
|
||||
};
|
||||
|
||||
// Add page
|
||||
createPage(page);
|
||||
};
|
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"outDir": "dist",
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"jsx": "react",
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"importHelpers": true,
|
||||
"noUnusedLocals": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strictNullChecks": true,
|
||||
"noImplicitAny": false,
|
||||
"moduleResolution": "node",
|
||||
"preserveConstEnums": true,
|
||||
"lib": ["es5", "dom"],
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"semi": "off"
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin')
|
||||
|
||||
module.exports = {
|
||||
stories: ['../src/**/*.story.tsx'],
|
||||
addons: ['@storybook/addon-actions', '@storybook/addon-links'],
|
||||
webpackFinal: async config => {
|
||||
config.module.rules.push({
|
||||
test: /\.(ts|tsx)$/,
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
presets: [['react-app', { flow: false, typescript: true }]],
|
||||
},
|
||||
})
|
||||
|
||||
config.plugins.push(
|
||||
new MonacoWebpackPlugin({
|
||||
languages: ['typescript'],
|
||||
})
|
||||
)
|
||||
config.resolve.extensions.push('.ts', '.tsx')
|
||||
return config
|
||||
},
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
|
||||
# Application Insights README
|
||||
Fluent website telementry information
|
||||
|
||||
**Table of Contents**
|
||||
<!-- TOC -->
|
||||
|
||||
- [Application Insights README](#application-insights-readme)
|
||||
- [Description](#description)
|
||||
- [Usage](#usage)
|
||||
- [IMPORTANT! Build notes](#important-build-notes)
|
||||
- [NPM Packages](#npm-packages)
|
||||
- [Resources](#resources)
|
||||
|
||||
<!-- /TOC -->
|
||||
|
||||
|
||||
## Description
|
||||
The Application Insights interface has be designed as a client side only react component. It hooks into the equivelent of
|
||||
onComponentDidMount event (via hooks) and runs once per instantiation.
|
||||
|
||||
## Usage
|
||||
|
||||
*PageView*
|
||||
```typescript
|
||||
import { usePageViewTelemetry } from '../components/ApplicationInsights'
|
||||
|
||||
...
|
||||
|
||||
let pathName = "Home" // if pathName is null, it will use the window.location.pathName value
|
||||
const [pageView, setPageView] = usePageViewTelemetry({ name: props.path })
|
||||
|
||||
// since the call is made immediately, you can just do the following if you are not updating the value
|
||||
usePageViewTelemetry({ name: props.path })
|
||||
|
||||
```
|
||||
*EventView*
|
||||
```jsx
|
||||
import { useEventTelemetry } from '../components/ApplicationInsights'
|
||||
|
||||
...
|
||||
|
||||
const [myEvent, invokeMyEventTelemetry] = useEventTelemetry({ name: 'MyEvent' })
|
||||
|
||||
const buttonClick = () => {
|
||||
invokeMyEventTelemetry();
|
||||
}
|
||||
|
||||
return (
|
||||
<button onClick={buttonClick}>Click Here!</button>
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
*EventView With Name/Value Property*
|
||||
```jsx
|
||||
import { useEventTelemetry } from '../components/ApplicationInsights'
|
||||
|
||||
...
|
||||
|
||||
const [myEvent, invokeMyEventTelemetry] = useEventTelemetry({ name: 'MyEvent' })
|
||||
|
||||
const sendEvent = (buttonId:number) => {
|
||||
myEvent.properties = myEvent.properties ? myEvent.properties : []
|
||||
myEvent.properties["Button_Clicked"] = buttonId
|
||||
// this call will send the update and send the telementry data
|
||||
invokeMyEventTelemetry(myEvent)
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<button onClick={() => {sendEvent(1)}}>Button 1</button>
|
||||
<button onClick={() => {sendEvent(2)}}>Button 2</button>
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
## IMPORTANT! Build notes
|
||||
*NOTE
|
||||
For production builds you need to set GATBSY_APPLICATIONINSIGHTS_KEY to the value of the production key *prior* to
|
||||
a production build. This can be done as an evironment variable or in the .env.production file under src/website.
|
||||
The key is retrieved from the Application Insights app on https://portal.azure.com
|
||||
|
||||
For development/test builds, modify the .env.developement file.
|
||||
|
||||
## NPM Packages
|
||||
NPM package(s):
|
||||
@microsoft/applicationinsights-web
|
||||
@microsoft/applicationinsights-react-js
|
||||
|
||||
## Resources
|
||||
[Azure Portal Resource](https://ms.portal.azure.com/#@microsoft.onmicrosoft.com/resource/subscriptions/9ccbac18-03d3-485b-a43e-87dc09014817/resourcegroups/OXOSharedRG/providers/microsoft.insights/components/FluentUI-Website/overview)
|
||||
|
||||
[Javascript NPM Setup](https://docs.microsoft.com/en-us/azure/azure-monitor/app/javascript#npm-based-setup)
|
||||
|
||||
[Application Insights React](https://github.com/microsoft/ApplicationInsights-JS/blob/17ef50442f73fd02a758fbd74134933d92607ecf/extensions/applicationinsights-react-js/README.md)
|
|
@ -1,69 +0,0 @@
|
|||
{
|
||||
"name": "gatsby-theme-fluent-site",
|
||||
"entries": [
|
||||
{
|
||||
"date": "Fri, 17 Apr 2020 22:36:03 GMT",
|
||||
"tag": "gatsby-theme-fluent-site_v0.2.0",
|
||||
"version": "0.2.0",
|
||||
"comments": {
|
||||
"minor": [
|
||||
{
|
||||
"comment": "yarn.lock",
|
||||
"author": "warleu@microsoft.com",
|
||||
"commit": "205e7b7e88151a17f2560ecdaec1c6e13c5825b7",
|
||||
"package": "gatsby-theme-fluent-site"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Thu, 09 Apr 2020 18:39:15 GMT",
|
||||
"tag": "gatsby-theme-fluent-site_v0.1.3",
|
||||
"version": "0.1.3",
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"comment": "merge conflicts",
|
||||
"author": "ppatboyd@outlook.com",
|
||||
"commit": "232a97794dc044cec9671e5cca4cf204d881f614",
|
||||
"package": "gatsby-theme-fluent-site"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Wed, 08 Apr 2020 21:26:12 GMT",
|
||||
"tag": "gatsby-theme-fluent-site_v0.1.2",
|
||||
"version": "0.1.2",
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"comment": "update package name",
|
||||
"author": "mgodbolt@microsoft.com",
|
||||
"commit": "232a97794dc044cec9671e5cca4cf204d881f614",
|
||||
"package": "gatsby-theme-fluent-site"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Sat, 27 Jul 2019 05:29:12 GMT",
|
||||
"tag": "gatsby-starter-uifabric-doc_v0.1.1",
|
||||
"version": "0.1.1",
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"comment": "initial publish",
|
||||
"author": "kchau@microsoft.com",
|
||||
"commit": "d5ff88bc7ddf21d5e9035f4a95503df50709f55c"
|
||||
},
|
||||
{
|
||||
"comment": "initial release",
|
||||
"author": "kchau@microsoft.com",
|
||||
"commit": "e9f0dccdd68a5890a3a063b79314d3ea446a95da"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
# Change Log - gatsby-theme-fluent-site
|
||||
|
||||
This log was last generated on Fri, 17 Apr 2020 22:36:03 GMT and should not be manually modified.
|
||||
|
||||
<!-- Start content -->
|
||||
|
||||
## 0.2.0
|
||||
|
||||
Fri, 17 Apr 2020 22:36:03 GMT
|
||||
|
||||
### Minor changes
|
||||
|
||||
- yarn.lock (warleu@microsoft.com)
|
||||
|
||||
## 0.1.3
|
||||
Thu, 09 Apr 2020 18:39:15 GMT
|
||||
|
||||
### Patches
|
||||
|
||||
- merge conflicts (ppatboyd@outlook.com)
|
||||
## 0.1.2
|
||||
Wed, 08 Apr 2020 21:26:12 GMT
|
||||
|
||||
### Patches
|
||||
|
||||
- update package name (mgodbolt@microsoft.com)
|
||||
## 0.1.1
|
||||
Sat, 27 Jul 2019 05:29:12 GMT
|
||||
|
||||
### Patches
|
||||
|
||||
- initial publish (kchau@microsoft.com)
|
||||
,- initial release (kchau@microsoft.com)
|
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2018 gatsbyjs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,86 +0,0 @@
|
|||
## 🚀 Quick start
|
||||
|
||||
|
||||
1. **Start developing.**
|
||||
|
||||
```sh
|
||||
yarn
|
||||
yarn start
|
||||
```
|
||||
|
||||
1. **Open the source code and start editing!**
|
||||
|
||||
Your site is now running at `http://localhost:3000`!
|
||||
|
||||
_Note: You'll also see a second link: _`http://localhost:3000/___graphql`_. This is a tool you can use to experiment with querying your data. Learn more about using this tool in the [Gatsby tutorial](https://www.gatsbyjs.org/tutorial/part-five/#introducing-graphiql)._
|
||||
|
||||
1. Working with [NetlifyCMS](https://www.netlifycms.org/)
|
||||
|
||||
NetlifyCMS is a React application that sites on top of the markdown content in git and provides a user friendly interface for creating, editing, and reviewing proposed content changes.
|
||||
|
||||
To develop new collections and work within NetlifyCMS's `static/admin/config.yml` file, you can now run a local server and allow the CMS to create and edit your local files
|
||||
|
||||
```sh
|
||||
npx netlify-cms-proxy-server
|
||||
# while server is running, in a seperate terminal run
|
||||
yarn start
|
||||
```
|
||||
|
||||
Now when you navigate to `http://localhost:3000/admin/` you will be allowed to log in without authentication, and any file change will only change your local data. No git involved.
|
||||
|
||||
|
||||
## 🧐 What's inside?
|
||||
|
||||
A quick look at the top-level files and directories you'll see in a the Website package.
|
||||
|
||||
.
|
||||
├── src
|
||||
├── gatsby-browser.js
|
||||
├── gatsby-config.js
|
||||
├── gatsby-node.js
|
||||
├── gatsby-ssr.js
|
||||
├── LICENSE
|
||||
├── package-lock.json
|
||||
├── package.json
|
||||
└── README.md
|
||||
|
||||
|
||||
|
||||
2. **`/src`**: This directory will contain all of the code related to what you will see on the front-end of your site (what you see in the browser) such as your site header or a page template. `src` is a convention for “source code”.
|
||||
|
||||
|
||||
5. **`gatsby-browser.js`**: This file is where Gatsby expects to find any usage of the [Gatsby browser APIs](https://www.gatsbyjs.org/docs/browser-apis/) (if any). These allow customization/extension of default Gatsby settings affecting the browser.
|
||||
|
||||
6. **`gatsby-config.js`**: This is the main configuration file for a Gatsby site. This is where you can specify information about your site (metadata) like the site title and description, which Gatsby plugins you’d like to include, etc. (Check out the [config docs](https://www.gatsbyjs.org/docs/gatsby-config/) for more detail).
|
||||
|
||||
7. **`gatsby-node.js`**: This file is where Gatsby expects to find any usage of the [Gatsby Node APIs](https://www.gatsbyjs.org/docs/node-apis/) (if any). These allow customization/extension of default Gatsby settings affecting pieces of the site build process.
|
||||
|
||||
8. **`gatsby-ssr.js`**: This file is where Gatsby expects to find any usage of the [Gatsby server-side rendering APIs](https://www.gatsbyjs.org/docs/ssr-apis/) (if any). These allow customization of default Gatsby settings affecting server-side rendering.
|
||||
|
||||
9. **`LICENSE`**: Gatsby is licensed under the MIT license.
|
||||
|
||||
11. **`package.json`**: A manifest file for Node.js projects, which includes things like metadata (the project’s name, author, etc). This manifest is how npm knows which packages to install for your project.
|
||||
|
||||
12. **`README.md`**: A text file containing useful reference information about your project.
|
||||
|
||||
## 🎓 Learning Gatsby
|
||||
|
||||
Looking for more guidance? Full documentation for Gatsby lives [on the website](https://www.gatsbyjs.org/). Here are some places to start:
|
||||
|
||||
- **For most developers, we recommend starting with our [in-depth tutorial for creating a site with Gatsby](https://www.gatsbyjs.org/tutorial/).** It starts with zero assumptions about your level of ability and walks through every step of the process.
|
||||
|
||||
- **To dive straight into code samples, head [to our documentation](https://www.gatsbyjs.org/docs/).** In particular, check out the _Guides_, _API Reference_, and _Advanced Tutorials_ sections in the sidebar.
|
||||
|
||||
## 💫 Deploy
|
||||
|
||||
[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/gatsbyjs/gatsby-starter-hello-world)
|
||||
|
||||
<!-- AUTO-GENERATED-CONTENT:END -->
|
||||
|
||||
## Developing Components
|
||||
|
||||
Check out [README](./src/components/CONTRIBUTING.md) in the components directory.
|
||||
|
||||
## Storybook
|
||||
|
||||
A playground for developing components in isolation [README](./src/components/STORYBOOK.md)
|
|
@ -1,145 +0,0 @@
|
|||
require('dotenv').config({
|
||||
path: `.env.${process.env.NODE_ENV}`,
|
||||
})
|
||||
module.exports = themeOptions => {
|
||||
const { contentPath, pathPrefix } = themeOptions
|
||||
return {
|
||||
pathPrefix: pathPrefix || '',
|
||||
siteMetadata: {
|
||||
title: 'Microsoft Design - Fluent',
|
||||
description:
|
||||
'Fluent brings the fundamentals of principled design, innovation in technology, and customer needs together as one. It’s a collective approach to creating simplicity and coherence through a shared, open design system across platforms.',
|
||||
siteURL: 'https://fluentui.z5.web.core.windows.net/',
|
||||
headerLinks: [
|
||||
{
|
||||
name: 'Fundamentals',
|
||||
link: '/fundamentals',
|
||||
headerOnly: true,
|
||||
},
|
||||
{
|
||||
name: 'Web',
|
||||
link: '/web',
|
||||
},
|
||||
{
|
||||
name: 'Windows',
|
||||
link: '/windows',
|
||||
},
|
||||
{
|
||||
name: 'iOS',
|
||||
link: '/ios',
|
||||
},
|
||||
{
|
||||
name: 'Android',
|
||||
link: '/android',
|
||||
},
|
||||
{
|
||||
name: 'Mac',
|
||||
link: '/mac',
|
||||
},
|
||||
],
|
||||
topLinks: [
|
||||
{ name: 'Get started', link: 'get-started' },
|
||||
{ name: 'Styles & Theming', link: 'styles' },
|
||||
{ name: 'Experiences', link: 'experiences' },
|
||||
{ name: 'Components', link: 'components' },
|
||||
],
|
||||
footerLinks: [
|
||||
{
|
||||
name: 'Resources',
|
||||
link: '/resources',
|
||||
ariaLabel: 'This link will take you to the Resources page',
|
||||
},
|
||||
{
|
||||
name: "What's new",
|
||||
link: '/whatsnew',
|
||||
ariaLabel: "This link will take you to the What's new page",
|
||||
},
|
||||
{
|
||||
name: 'GitHub',
|
||||
link: 'https://github.com/microsoft/fluent-site',
|
||||
target: '_blank',
|
||||
ariaLabel: 'This link will take you to the Microsoft Fluent UI GitHub site in a new window.',
|
||||
},
|
||||
{
|
||||
name: 'Privacy & cookies',
|
||||
link: 'https://privacy.microsoft.com/en-us/privacystatement',
|
||||
ariaLabel: 'This link will take you to the Microsoft privacy statement.',
|
||||
},
|
||||
],
|
||||
homePageData: {
|
||||
news: [
|
||||
{
|
||||
title: 'Lorem ipsum dolor sit amet, consectet adipiscing elit. Vivamus ut max velit, ut iaculis est. Nullam tincidunt.',
|
||||
link: '#',
|
||||
},
|
||||
{
|
||||
title: 'Lorem ipsum dolor sit amet, consectet adipiscing elit. Vivamus ut max velit, ut iaculis est. Nullam tincidunt.',
|
||||
link: '#',
|
||||
},
|
||||
{
|
||||
title: 'Lorem ipsum dolor sit amet, consectet adipiscing elit. Vivamus ut max velit, ut iaculis est. Nullam tincidunt.',
|
||||
link: '#',
|
||||
},
|
||||
{
|
||||
title: 'Lorem ipsum dolor sit amet, consectet adipiscing elit. Vivamus ut max velit, ut iaculis est. Nullam tincidunt.',
|
||||
link: '#',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
`gatsby-plugin-emotion`,
|
||||
`gatsby-transformer-yaml`,
|
||||
`gatsby-plugin-react-helmet`,
|
||||
`gatsby-plugin-typescript`,
|
||||
'gatsby-plugin-sharp',
|
||||
'gatsby-transformer-sharp',
|
||||
`gatsby-plugin-offline`,
|
||||
{
|
||||
resolve: `gatsby-plugin-netlify-cms`,
|
||||
options: {
|
||||
modulePath: `${__dirname}/src/cms/cms.js`,
|
||||
},
|
||||
},
|
||||
{
|
||||
resolve: `gatsby-plugin-manifest`,
|
||||
options: {
|
||||
name: `Fabric Website 2.0`,
|
||||
short_name: `fabricwebsite`,
|
||||
start_url: `/`,
|
||||
background_color: `#f7f0eb`,
|
||||
theme_color: `#a2466c`,
|
||||
display: `standalone`,
|
||||
},
|
||||
},
|
||||
{
|
||||
resolve: `gatsby-plugin-mdx`,
|
||||
options: {
|
||||
defaultLayouts: {
|
||||
default: require.resolve('./src/templates/MDXTemplate.tsx'),
|
||||
},
|
||||
gatsbyRemarkPlugins: [
|
||||
{
|
||||
resolve: `gatsby-remark-images`,
|
||||
options: {
|
||||
maxWidth: 400,
|
||||
withWebp: true,
|
||||
tracedSVG: true,
|
||||
linkImagesToOriginal: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
resolve: `gatsby-remark-copy-linked-files`,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
resolve: `gatsby-plugin-docs-creator`,
|
||||
options: {
|
||||
path: contentPath,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
const path = require('path')
|
||||
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin')
|
||||
|
||||
exports.onCreatePage = ({ page, actions }) => {
|
||||
const { createPage, deletePage } = actions
|
||||
}
|
||||
|
||||
exports.createPages = async ({ actions, graphql }) => {
|
||||
const { createPage } = actions
|
||||
}
|
||||
|
||||
exports.onCreateWebpackConfig = ({ stage, actions }) => {
|
||||
if (stage.startsWith('develop')) {
|
||||
actions.setWebpackConfig({
|
||||
resolve: {
|
||||
alias: {
|
||||
'react-dom': '@hot-loader/react-dom',
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
actions.setWebpackConfig({
|
||||
plugins: [
|
||||
new MonacoWebpackPlugin({
|
||||
languages: ['typescript'],
|
||||
}),
|
||||
],
|
||||
})
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
import { Stylesheet, InjectionMode } from '@uifabric/merge-styles'
|
||||
import { renderStatic } from '@uifabric/merge-styles/lib/server'
|
||||
import { renderToString } from 'react-dom/server'
|
||||
import React from 'react'
|
||||
|
||||
const config = require('./gatsby-config')
|
||||
|
||||
export const replaceRenderer = ({ bodyComponent, replaceBodyHTMLString, setHeadComponents }) => {
|
||||
const { html, css } = renderStatic(() => {
|
||||
return renderToString(bodyComponent)
|
||||
})
|
||||
|
||||
replaceBodyHTMLString(html)
|
||||
|
||||
setHeadComponents([<style dangerouslySetInnerHTML={{ __html: css }} />])
|
||||
}
|
||||
|
||||
export const onRenderBody = ({ pathname, setHeadComponents }) => {
|
||||
setHeadComponents([<link rel="canonical" href={`${config.siteMetadata ? config.siteMetadata.siteUrl : '/'}${pathname}`} />])
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
{
|
||||
"name": "gatsby-theme-fluent-site",
|
||||
"version": "0.2.0",
|
||||
"main": "index.js",
|
||||
"description": "A Fluent theme for GatsbyJS",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/microsoft/fluent-site"
|
||||
},
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"clean": "gatsby clean",
|
||||
"build": "echo 'building'",
|
||||
"start": "echo 'starting'",
|
||||
"test": "echo \"Write tests! -> https://gatsby.app/unit-testing\"",
|
||||
"storybook": "start-storybook -p 6006",
|
||||
"build-storybook": "build-storybook"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.8.3",
|
||||
"@mdx-js/mdx": "^1.1.0",
|
||||
"@mdx-js/react": "^1.0.27",
|
||||
"@storybook/addon-actions": "^5.3.4",
|
||||
"@storybook/addon-links": "^5.3.4",
|
||||
"@storybook/addons": "^5.3.4",
|
||||
"@storybook/react": "^5.3.4",
|
||||
"@types/graphql": "^14.2.0",
|
||||
"@types/node": "^11.13.13",
|
||||
"@types/react": "^16.8.19",
|
||||
"@types/react-dom": "^16.8.4",
|
||||
"@types/react-helmet": "^5.0.8",
|
||||
"babel-loader": "^8.0.6",
|
||||
"gatsby": "^2.20.10",
|
||||
"gatsby-image": "^2.2.38",
|
||||
"gatsby-link": "^2.2.2",
|
||||
"gatsby-plugin-docs-creator": "^2.2.0",
|
||||
"gatsby-plugin-manifest": "^2.2.20",
|
||||
"gatsby-plugin-mdx": "^1.0.67",
|
||||
"gatsby-plugin-netlify-cms": "^4.2.2",
|
||||
"gatsby-plugin-offline": "^3.0.32",
|
||||
"gatsby-plugin-react-helmet": "^3.1.21",
|
||||
"gatsby-plugin-sharp": "^2.3.13",
|
||||
"gatsby-plugin-typescript": "^2.1.26",
|
||||
"gatsby-remark-copy-linked-files": "^2.1.36",
|
||||
"gatsby-remark-images": "^3.1.42",
|
||||
"gatsby-remark-prismjs": "^3.3.30",
|
||||
"gatsby-source-filesystem": "^2.1.46",
|
||||
"gatsby-source-git": "^1.0.2",
|
||||
"gatsby-transformer-remark": "^2.6.48",
|
||||
"gatsby-transformer-sharp": "^2.3.12",
|
||||
"gatsby-transformer-yaml": "^2.2.24",
|
||||
"monaco-editor-webpack-plugin": "^1.9.0",
|
||||
"netlify-cms-app": "^2.12.2",
|
||||
"office-ui-fabric-react": "^7.92.0",
|
||||
"prism-react-renderer": "^1.0.1",
|
||||
"react": "^16.13.0",
|
||||
"react-dom": "^16.13.0",
|
||||
"react-helmet": "^5.2.1",
|
||||
"react-live": "^2.1.2",
|
||||
"react-monaco-editor": "^0.34.0",
|
||||
"remark-parse": "^7.0.0",
|
||||
"remark-react": "^6.0.0",
|
||||
"unified": "^8.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/core": "^10.0.27",
|
||||
"@emotion/styled": "^10.0.27",
|
||||
"@hot-loader/react-dom": "^16.12.0",
|
||||
"@loadable/component": "^5.12.0",
|
||||
"@microsoft/applicationinsights-react-js": "^2.4.4",
|
||||
"@microsoft/applicationinsights-web": "^2.4.4",
|
||||
"@uifabric/api-docs": "^7.2.13",
|
||||
"@uifabric/example-app-base": "^7.11.16",
|
||||
"fuse.js": "^3.4.6",
|
||||
"gatsby-plugin-emotion": "^4.1.22",
|
||||
"monaco-editor": "^0.20.0",
|
||||
"typescript": "^3.5.1"
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
/**
|
||||
* The default export of `netlify-cms-app` is an object with all of the Netlify CMS
|
||||
* extension registration methods, such as `registerWidget` and
|
||||
* `registerPreviewTemplate`.
|
||||
*/
|
||||
import CMS from 'netlify-cms-app'
|
|
@ -1,65 +0,0 @@
|
|||
import React, { useEffect } from 'react'
|
||||
import { ApplicationInsights } from '@microsoft/applicationinsights-web'
|
||||
import styled from '@emotion/styled'
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
appInsights: ApplicationInsights
|
||||
}
|
||||
}
|
||||
|
||||
const StyledAppInsights = styled.div`
|
||||
display: none;
|
||||
`
|
||||
export interface IAppInsightsPageViewProps {
|
||||
path: string | null
|
||||
}
|
||||
|
||||
export interface IAppInsightsEventViewProps {
|
||||
eventName: string
|
||||
eventPropertyName?: string
|
||||
eventPropertyValue?: any
|
||||
}
|
||||
|
||||
const AppInsightsLoadable = (props: IAppInsightsPageViewProps | IAppInsightsEventViewProps) => {
|
||||
useEffect(() => {
|
||||
if (window.appInsights === undefined) {
|
||||
/* TODO: The key needs to be injected for production vs development */
|
||||
window.appInsights = new ApplicationInsights({
|
||||
config: {
|
||||
instrumentationKey: `${process.env.GATSBY_APPLICATIONINSIGHTS_KEY}`,
|
||||
enableAutoRouteTracking: true,
|
||||
|
||||
/* ...Other Configuration Options... */
|
||||
},
|
||||
})
|
||||
|
||||
window.appInsights.loadAppInsights()
|
||||
}
|
||||
let eventProps = props as IAppInsightsEventViewProps
|
||||
if (eventProps.eventName !== undefined) {
|
||||
if (eventProps.eventPropertyName !== undefined) {
|
||||
window.appInsights.trackEvent({
|
||||
name: eventProps.eventName,
|
||||
properties: [eventProps.eventPropertyName] = eventProps.eventPropertyValue,
|
||||
})
|
||||
} else {
|
||||
window.appInsights.trackEvent({ name: eventProps.eventName })
|
||||
}
|
||||
} else {
|
||||
let pageViewProps = props as IAppInsightsPageViewProps
|
||||
let path = pageViewProps.path ? pageViewProps.path : window.location.pathname
|
||||
if (path === '/') {
|
||||
path = document.title
|
||||
} else {
|
||||
path = path.replace(/^\//, '')
|
||||
}
|
||||
if (path) {
|
||||
window.appInsights.trackPageView({ name: path })
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
return <StyledAppInsights />
|
||||
}
|
||||
export default AppInsightsLoadable
|
|
@ -1,36 +0,0 @@
|
|||
import { ApplicationInsights, IEventTelemetry } from '@microsoft/applicationinsights-web'
|
||||
import React from 'react'
|
||||
|
||||
interface AppInsightsWrapper {
|
||||
appInsights: ApplicationInsights
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
appInsights: ApplicationInsights
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns an instance of the Application Insights object, undefined, or false(during SSR)
|
||||
**/
|
||||
export default function InitAppInsights(): ApplicationInsights | undefined {
|
||||
const windowGlobal = (typeof window !== 'undefined' && window) as Window
|
||||
const key = `${process.env.GATSBY_APPLICATIONINSIGHTS_KEY}`
|
||||
if (windowGlobal && key !== undefined && key !== '') {
|
||||
if (windowGlobal.appInsights === undefined) {
|
||||
/* TODO: The key needs to be injected for production vs development */
|
||||
windowGlobal.appInsights = new ApplicationInsights({
|
||||
config: {
|
||||
instrumentationKey: key,
|
||||
enableAutoRouteTracking: true,
|
||||
|
||||
/* ...Other Configuration Options... */
|
||||
},
|
||||
})
|
||||
|
||||
windowGlobal.appInsights.loadAppInsights()
|
||||
}
|
||||
return windowGlobal.appInsights
|
||||
}
|
||||
return undefined
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
import useEventTelemetry from './useEventTelemetry'
|
||||
import usePageViewTelemetry from './usePageViewTelemetry'
|
||||
export { useEventTelemetry, usePageViewTelemetry }
|
|
@ -1,33 +0,0 @@
|
|||
import { useState, useEffect, Dispatch, SetStateAction } from 'react'
|
||||
import { IEventTelemetry } from '@microsoft/applicationinsights-web'
|
||||
|
||||
import InitAppInsights from './InitAppInsights'
|
||||
|
||||
//#region IEventTelemetry Hook
|
||||
/**
|
||||
* Returns a stateful value of a IEventTelemetry object and a function to invoke the telemetry call
|
||||
* @param {IEventTelemetry} data IEventTelemetry Object
|
||||
* @returns [IEventTelemetry, Function to invoke the call]
|
||||
**/
|
||||
export default function usePageViewTelemetry(
|
||||
data: IEventTelemetry | undefined
|
||||
): [IEventTelemetry, Dispatch<SetStateAction<IEventTelemetry | undefined>>] {
|
||||
data = data || ({} as IEventTelemetry)
|
||||
const [telementryData, setTelementryData] = useState(data)
|
||||
|
||||
const invoke = (newData: IEventTelemetry | undefined) => {
|
||||
if (newData !== undefined) {
|
||||
setTelementryData(newData)
|
||||
}
|
||||
newData = newData || telementryData
|
||||
if (newData !== undefined) {
|
||||
let appInsights = InitAppInsights()
|
||||
if (appInsights) {
|
||||
appInsights.trackEvent(newData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [telementryData, invoke]
|
||||
}
|
||||
//#endregion
|
|
@ -1,41 +0,0 @@
|
|||
import { useState, useEffect, Dispatch, SetStateAction } from 'react'
|
||||
import { IPageViewTelemetry } from '@microsoft/applicationinsights-web'
|
||||
|
||||
import InitAppInsights from './InitAppInsights'
|
||||
|
||||
//#region usePageViewTelemetry Hook
|
||||
/**
|
||||
* Returns a stateful value of a IPageViewTelemetry object and a function to update the telemetry value
|
||||
* @param {IPageViewTelemetry} data IPageViewTelemetry Object
|
||||
* @returns [IPageViewTelemetry, Function to update]
|
||||
**/
|
||||
export default function usePageViewTelemetry(
|
||||
data: IPageViewTelemetry | undefined
|
||||
): [IPageViewTelemetry | undefined, Dispatch<SetStateAction<IPageViewTelemetry | undefined>>] {
|
||||
// Send the data immediately (OnComponentMount) if instantiated with data
|
||||
const [telementryData, setTelementryData] = useState(data)
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window !== undefined && window && telementryData !== undefined) {
|
||||
let appInsights = InitAppInsights()
|
||||
if (appInsights !== undefined) {
|
||||
let path = telementryData.name
|
||||
if (path === undefined) {
|
||||
path = window.location.pathname
|
||||
if (path === '/') {
|
||||
path = document.title
|
||||
} else {
|
||||
path = path.replace(/^\//, '')
|
||||
}
|
||||
telementryData.name = path
|
||||
setTelementryData(telementryData)
|
||||
}
|
||||
appInsights.trackPageView(telementryData)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
return [telementryData, setTelementryData]
|
||||
}
|
||||
|
||||
//#endregion
|
|
@ -1,38 +0,0 @@
|
|||
# A WIP guide to contributing components
|
||||
|
||||
> Just gathering some thoughts and notes. These are not final, may change with additional clarity, and are open for discussion. - Scott
|
||||
|
||||
## Notes for components:
|
||||
1. The UI for the docs site should be built using Fluent UI. Seems like table stakes.
|
||||
2. Initially, we will need to scaffold out the site using components built with JSX tags.
|
||||
3. Some level of run-time theming will need to be supported.
|
||||
4. Theming should leverage the same Fluent UI base styles.
|
||||
- Possible themes include high contrast, dark mode, etc.
|
||||
|
||||
## Component questions / thoughts:
|
||||
|
||||
1. Can we use the Fluent UI components directly in code or do we need a proxy component to make global changes easier?
|
||||
|
||||
> I am leaning on using the Fluent UI components directly since we will be custom-building a version of it for the docs site. - Scott
|
||||
|
||||
2. Does overall site theming affect the theme that is shown on any given component?
|
||||
3. Is global / local theming a setting somewhere?
|
||||
4. How are design-time customizations exposed for use in the site?
|
||||
|
||||
---
|
||||
|
||||
## Notes for CSS variables:
|
||||
- There seems to be a logical divide between theme styles that are static and dynamic.
|
||||
- Some properties need to be changed at runtime. This includes color themes, density, and others.
|
||||
- Some properties only need to be changed at design time. This includes most other aspects of the design system: ramps (type, spacing, color), elevation, etc.
|
||||
- Fallback values will need to provided in a way that allows legacy browser to style things correctly.
|
||||
- Fallbacks can use the `var(--bg-color, white)` syntax.
|
||||
- Or they can be made more robust w/ graceful degredation by declaring legacy properties alongside variable properties.
|
||||
- When leveraging CSS variables, media queries are used to change the value of custom properties.
|
||||
- This separates out the styling blocks from the layout logic.
|
||||
- It might make sense to use special casing for global CSS variables, e.g. `--PAGE-BG-COLOR` to more easily differentiate them from local variables.
|
||||
|
||||
## CSS variables questions / thoughts:
|
||||
|
||||
1. How do we reconcile runtime styling and design time styling?
|
||||
- The site will need some flexibility that the compiled library won’t need. For example, the theme editor with need to change design-time properties on the fly, but implementations of Fluent UI will not introduce these aspects as runtime properties.
|
|
@ -1,13 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import styled from '@emotion/styled'
|
||||
|
||||
export const PageInnerContent = props => {
|
||||
return <StyledContent>{props.children}</StyledContent>
|
||||
}
|
||||
|
||||
const StyledContent = styled.main`
|
||||
border-left: 1px solid #eee;
|
||||
padding: 40px;
|
||||
overflow-y: auto;
|
||||
flex-grow: 1;
|
||||
`
|
|
@ -1 +0,0 @@
|
|||
export { PageInnerContent } from './PageInnerContent'
|
|
@ -1,37 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import { useStaticQuery, graphql } from 'gatsby'
|
||||
import styled from '@emotion/styled'
|
||||
import { FooterMenuItems } from './FooterMenuItems'
|
||||
|
||||
export const Footer = props => {
|
||||
const data = useStaticQuery(graphql`
|
||||
query FooterMenuQuery {
|
||||
site {
|
||||
siteMetadata {
|
||||
footerLinks {
|
||||
name
|
||||
link
|
||||
target
|
||||
ariaLabel
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
const {
|
||||
site: { siteMetadata },
|
||||
} = data
|
||||
return (
|
||||
<StyledFooter>
|
||||
<FooterMenuItems {...siteMetadata} />
|
||||
</StyledFooter>
|
||||
)
|
||||
}
|
||||
|
||||
const StyledFooter = styled.div`
|
||||
display: flex;
|
||||
margin: 0 auto;
|
||||
padding: 40px 40px 40px 20px;
|
||||
border-top: 1px solid #eee;
|
||||
`
|
|
@ -1,62 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import styled from '@emotion/styled'
|
||||
import { Link } from 'gatsby'
|
||||
|
||||
export const FooterMenuItems = props => {
|
||||
return (
|
||||
<StyledMenuItems>
|
||||
{props.footerLinks.map((link, idx) => (
|
||||
<MenuItem {...link} key={'footerItem_' + idx} />
|
||||
))}
|
||||
</StyledMenuItems>
|
||||
)
|
||||
}
|
||||
|
||||
const MenuItem = props => {
|
||||
return (
|
||||
<StyledMenuItem key={props.name}>
|
||||
{props.link.startsWith('http') ? (
|
||||
<a
|
||||
href={props.link}
|
||||
target={props.target !== undefined ? props.target : undefined}
|
||||
{...{ 'aria-label': props.ariaLabel ? props.ariaLabel : undefined }}
|
||||
>
|
||||
{props.name}
|
||||
</a>
|
||||
) : (
|
||||
<Link
|
||||
activeClassName="Link-IsActive"
|
||||
to={props.link}
|
||||
target={props.target ? props.target : undefined}
|
||||
{...{ 'aria-label': props.ariaLabel ? props.ariaLabel : undefined }}
|
||||
>
|
||||
{props.name}
|
||||
</Link>
|
||||
)}
|
||||
</StyledMenuItem>
|
||||
)
|
||||
}
|
||||
|
||||
const StyledMenuItems = styled.ul`
|
||||
display: flex;
|
||||
padding: 0;
|
||||
`
|
||||
|
||||
const StyledMenuItem = styled.li`
|
||||
list-style: none;
|
||||
margin: auto 10px;
|
||||
|
||||
a {
|
||||
color: #000;
|
||||
opacity: 0.7;
|
||||
padding: 0 2px;
|
||||
text-decoration: none;
|
||||
|
||||
&.Link-IsActive {
|
||||
border-bottom: 3px solid #000;
|
||||
padding-bottom: 20px;
|
||||
font-weight: 600;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
`
|
|
@ -1 +0,0 @@
|
|||
export { Footer } from './Footer'
|
|
@ -1,85 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import styled from '@emotion/styled'
|
||||
import { useStaticQuery, graphql, Link } from 'gatsby'
|
||||
import { HeaderMenuItems } from '.'
|
||||
import { Search } from '../Search'
|
||||
import { SubNav } from './SubNav'
|
||||
import { usePageContext } from '../Provider'
|
||||
|
||||
export const Header = props => {
|
||||
const pageContext = usePageContext()
|
||||
const data = useStaticQuery(graphql`
|
||||
query MainMenuQuery {
|
||||
site {
|
||||
siteMetadata {
|
||||
title
|
||||
headerLinks {
|
||||
name
|
||||
link
|
||||
}
|
||||
topLinks {
|
||||
link
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
const {
|
||||
pathContext: { rootPath },
|
||||
location: { pathname },
|
||||
} = pageContext
|
||||
|
||||
const {
|
||||
site: { siteMetadata },
|
||||
} = data
|
||||
|
||||
const topLinks = siteMetadata.topLinks.map(item => {
|
||||
return {
|
||||
name: item.name,
|
||||
link: '/' + rootPath + '/' + item.link,
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<StyledHeader>
|
||||
<Nav>
|
||||
<Logo to="/">
|
||||
<img src={require('gatsby-theme-fluent-site/static/images/microsoft.svg')} alt="Microsoft Logo" />
|
||||
<p>Fluent</p>
|
||||
</Logo>
|
||||
<HeaderMenuItems {...siteMetadata} />
|
||||
<Search />
|
||||
</Nav>
|
||||
{rootPath && rootPath !== 'fundamentals' && <SubNav topLinks={topLinks} />}
|
||||
</StyledHeader>
|
||||
)
|
||||
}
|
||||
|
||||
const StyledHeader = styled.div``
|
||||
|
||||
const Nav = styled.header`
|
||||
width: 100%;
|
||||
padding: 20px 40px;
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
border-bottom: 1px solid #eee;
|
||||
`
|
||||
|
||||
const Logo = styled(Link)`
|
||||
display: flex;
|
||||
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
|
||||
img {
|
||||
width: 22px;
|
||||
}
|
||||
p {
|
||||
margin: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
`
|
|
@ -1,63 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import styled from '@emotion/styled'
|
||||
import { Link } from 'gatsby'
|
||||
|
||||
const MenuItem = props => {
|
||||
return (
|
||||
<StyledMenuItem key={props.name}>
|
||||
<Link activeClassName="Link-IsActive" to={props.link}>
|
||||
{props.name}
|
||||
</Link>
|
||||
</StyledMenuItem>
|
||||
)
|
||||
}
|
||||
|
||||
export const HeaderMenuItems = props => {
|
||||
return (
|
||||
<StyledMenuItems>
|
||||
{props.headerLinks.map((link, i) => (
|
||||
<MenuItem key={i} {...link} />
|
||||
))}
|
||||
</StyledMenuItems>
|
||||
)
|
||||
}
|
||||
|
||||
const StyledMenuItems = styled.ul`
|
||||
display: flex;
|
||||
padding: 0;
|
||||
`
|
||||
|
||||
const StyledMenuItem = styled.li`
|
||||
list-style: none;
|
||||
margin: auto 10px;
|
||||
|
||||
&:first-of-type {
|
||||
border-right: 1px solid #eee;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #666;
|
||||
padding: 0 2px;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
|
||||
&.Link-IsActive {
|
||||
color: #000;
|
||||
font-weight: 500;
|
||||
position: relative;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -35px;
|
||||
left: calc(50% - 10px);
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-width: 0 10px 10px 10px;
|
||||
border-color: transparent transparent #eeeeee transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
|
@ -1,54 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import styled from '@emotion/styled'
|
||||
import { Link } from 'gatsby'
|
||||
|
||||
export const SubNav = props => {
|
||||
const menuItems = props.topLinks.map((item, index) => {
|
||||
return (
|
||||
<li key={index}>
|
||||
<Link partiallyActive={true} activeClassName="Link-IsActive" to={item.link}>
|
||||
{item.name}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
|
||||
return (
|
||||
<StyledSubNav>
|
||||
<StyledList>{menuItems}</StyledList>
|
||||
</StyledSubNav>
|
||||
)
|
||||
}
|
||||
|
||||
const StyledSubNav = styled.div`
|
||||
background-color: #eee;
|
||||
margin: 0px;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
padding: 40px;
|
||||
opacity: 0.4;
|
||||
}
|
||||
`
|
||||
|
||||
const StyledList = styled.ul`
|
||||
display: flex;
|
||||
padding: 30px 30px 30px 20px;
|
||||
max-width: 600px;
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
margin: 0 20px;
|
||||
|
||||
a {
|
||||
color: #666;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
|
||||
&.Link-IsActive {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
|
@ -1,2 +0,0 @@
|
|||
export { Header } from './Header'
|
||||
export { HeaderMenuItems } from './HeaderMenuItems'
|
|
@ -1,21 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import Highlight, { defaultProps } from 'prism-react-renderer'
|
||||
import darkTheme from 'prism-react-renderer/themes/nightOwl'
|
||||
|
||||
export const HighlightHOC = p => {
|
||||
return (
|
||||
<Highlight {...defaultProps} theme={darkTheme} code={p.children} language="jsx">
|
||||
{({ className, style, tokens, getLineProps, getTokenProps }) => (
|
||||
<div className={className} style={style}>
|
||||
{tokens.map((line, i) => (
|
||||
<div {...getLineProps({ line, key: i })}>
|
||||
{line.map((token, key) => (
|
||||
<span {...getTokenProps({ token, key })} />
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</Highlight>
|
||||
)
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import Highlight, { defaultProps } from 'prism-react-renderer'
|
||||
import darkTheme from 'prism-react-renderer/themes/nightOwl'
|
||||
|
||||
export const HighlightInlineHOC = p => {
|
||||
return (
|
||||
<Highlight {...defaultProps} theme={darkTheme} code={p.children} language="jsx">
|
||||
{({ className, style, tokens, getLineProps, getTokenProps }) => (
|
||||
<code className={className} style={style}>
|
||||
{tokens.map((line, i) => (
|
||||
<span {...getLineProps({ line, key: i })}>
|
||||
{line.map((token, key) => (
|
||||
<span {...getTokenProps({ token, key })} />
|
||||
))}
|
||||
</span>
|
||||
))}
|
||||
</code>
|
||||
)}
|
||||
</Highlight>
|
||||
)
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
export { HighlightInlineHOC as HighlightInline } from './HighlightInline'
|
||||
export { HighlightHOC as Highlight } from './Highlight'
|
|
@ -1,42 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import { css } from '@emotion/core'
|
||||
|
||||
interface NYIProps {
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
export const NYI = (props: NYIProps) => (
|
||||
<>
|
||||
<abbr
|
||||
css={css`
|
||||
display: inline-block;
|
||||
|
||||
background-color: lightyellow;
|
||||
padding: 1px 8px 1px 8px;
|
||||
border: 1px solid orange;
|
||||
border-radius: 2px;
|
||||
|
||||
color: black;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
`}
|
||||
title="Not Yet Implemented"
|
||||
>
|
||||
NYI
|
||||
</abbr>
|
||||
{props.children && (
|
||||
<span
|
||||
css={css`
|
||||
color: gray;
|
||||
font-style: italic;
|
||||
`}
|
||||
>
|
||||
{props.children}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)
|
|
@ -1,28 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import { css } from '@emotion/core'
|
||||
|
||||
interface PlaceholderProps {
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
export const Placeholder = (props: PlaceholderProps) => (
|
||||
<div
|
||||
css={css`
|
||||
display: flex;
|
||||
position: relative;
|
||||
height: 360px;
|
||||
|
||||
color: #808080;
|
||||
background-color: #f3f2f1;
|
||||
`}
|
||||
>
|
||||
<div
|
||||
css={css`
|
||||
display: block;
|
||||
margin: auto;
|
||||
`}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
|
@ -1,2 +0,0 @@
|
|||
export { NYI } from './NYI'
|
||||
export { Placeholder } from './Placeholder'
|
|
@ -1,14 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import styled from '@emotion/styled'
|
||||
|
||||
export const Persona = () => {
|
||||
return <StyledPersona>{/* Beep boop */}</StyledPersona>
|
||||
}
|
||||
|
||||
const StyledPersona = styled.div`
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin: 8px;
|
||||
background: #ccc;
|
||||
border-radius: 100%;
|
||||
`
|
|
@ -1,120 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import { IFrameRenderer } from './IFrameRenderer'
|
||||
import { usePlayground, IExample } from './context'
|
||||
|
||||
export const ExamplePreview = ({ example }: { example: IExample }) => {
|
||||
const [Component, error] = useTranspiledComponent(example.source)
|
||||
const [playground] = usePlayground()
|
||||
return (
|
||||
<IFrameRenderer>
|
||||
{ctx => {
|
||||
if (error) return null // TODO: better UX
|
||||
if (!Component) return null
|
||||
return (
|
||||
<ExampleRenderer>
|
||||
<Component document={ctx.document} theme={playground.currentTheme} rtl={playground.rtl} />
|
||||
</ExampleRenderer>
|
||||
)
|
||||
}}
|
||||
</IFrameRenderer>
|
||||
)
|
||||
}
|
||||
|
||||
const ExampleRenderer = ({ children }) => {
|
||||
const [playground] = usePlayground()
|
||||
|
||||
const style: any = {
|
||||
direction: playground.rtl ? 'rtl' : 'ltr',
|
||||
zoom: playground.zoomLevel,
|
||||
}
|
||||
if (playground.resolution !== 'Responsive') {
|
||||
style.width = playground.resolution
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
maxWidth: '100%',
|
||||
maxHeight: '100%',
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
<div style={style}>{children}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Transpiles source code into a React component. Expects the source code to
|
||||
* expose the component as a default export.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* const Component = useTranspiledComponent(`
|
||||
* import * as React from "react"
|
||||
*
|
||||
* export default () => {
|
||||
* return <h1>Hello World</h1>
|
||||
* }
|
||||
* `)
|
||||
*
|
||||
* // Component = () => React.createElement("h1", null, "Hello World")
|
||||
* ```
|
||||
*/
|
||||
function useTranspiledComponent(source: string): [React.ComponentType<any> | undefined, Error | undefined] {
|
||||
const ts = useLazyTypeScript()
|
||||
|
||||
// TypeScript has not loaded, do nothing.
|
||||
if (!ts) return [undefined, undefined]
|
||||
|
||||
// TODO: proper import handling... Consider TS VFS.
|
||||
source = source.replace('import * as React from "react"', '')
|
||||
source = source.replace('export default', 'return')
|
||||
|
||||
try {
|
||||
source = ts.transpileModule(source, {
|
||||
compilerOptions: {
|
||||
module: 'none',
|
||||
jsx: 'react',
|
||||
},
|
||||
}).outputText
|
||||
|
||||
// TODO: should this be sandboxed in some way?
|
||||
window.React = React // TODO: properly bind React into component scope.
|
||||
const Component = new Function(source)()
|
||||
|
||||
return [
|
||||
// Dumb wrapper around user-defined component to handle errors/undefined return value.
|
||||
// TODO: proper UX for errors
|
||||
props => {
|
||||
try {
|
||||
return Component(props) || null
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
},
|
||||
undefined,
|
||||
]
|
||||
} catch (e) {
|
||||
return [undefined, e]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy loads TypeScript for in-browser transpilation
|
||||
*/
|
||||
function useLazyTypeScript() {
|
||||
const [ts, setTS] = React.useState()
|
||||
React.useEffect(() => {
|
||||
let cancelled = false
|
||||
import('typescript').then(res => {
|
||||
if (!cancelled) {
|
||||
setTS(res.default)
|
||||
}
|
||||
})
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [])
|
||||
return ts
|
||||
}
|
|
@ -1,217 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import styled from '@emotion/styled'
|
||||
import CodeEditor from './plugins/CodeEditor'
|
||||
import ThemeEditor from './plugins/ThemeEditor'
|
||||
import { usePlayground } from './context'
|
||||
|
||||
const PLUGINS = [CodeEditor, ThemeEditor]
|
||||
|
||||
export const Footer = () => {
|
||||
return (
|
||||
<StyledFooter>
|
||||
<StyledFooterBanner>
|
||||
<PluginList />
|
||||
<StyledGrid>
|
||||
<RTLToggle />
|
||||
<ThemeSelector />
|
||||
</StyledGrid>
|
||||
</StyledFooterBanner>
|
||||
<ActivePluginOutlet />
|
||||
</StyledFooter>
|
||||
)
|
||||
}
|
||||
|
||||
const PluginList = () => {
|
||||
const [playground, dispatch] = usePlayground()
|
||||
return (
|
||||
<StyledPluginList>
|
||||
{PLUGINS.map(plugin => {
|
||||
const active = plugin === playground.currentPlugin
|
||||
return (
|
||||
<li
|
||||
key={plugin.label}
|
||||
data-is-active={active}
|
||||
onClick={() => {
|
||||
dispatch({ type: 'TOGGLE_PLUGIN', payload: plugin })
|
||||
}}
|
||||
>
|
||||
{plugin.icon}
|
||||
{plugin.label}
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</StyledPluginList>
|
||||
)
|
||||
}
|
||||
|
||||
const RTLToggle = () => {
|
||||
const [playground, dispatch] = usePlayground()
|
||||
return (
|
||||
<StyledInputLabel htmlFor="rtl">
|
||||
<span className="label">RTL</span>
|
||||
<StyledToggleSlider>
|
||||
<input
|
||||
id="rtl"
|
||||
type="checkbox"
|
||||
checked={playground.rtl}
|
||||
onChange={() => {
|
||||
dispatch({ type: 'TOGGLE_RTL' })
|
||||
}}
|
||||
/>
|
||||
<span className="slider" />
|
||||
</StyledToggleSlider>
|
||||
</StyledInputLabel>
|
||||
)
|
||||
}
|
||||
|
||||
const ThemeSelector = () => {
|
||||
const [playground, dispatch] = usePlayground()
|
||||
return (
|
||||
<StyledInputLabel htmlFor="theme">
|
||||
<span className="label">Theme</span>
|
||||
<StyledSelect
|
||||
id="theme"
|
||||
value={playground.currentTheme}
|
||||
onChange={e => {
|
||||
dispatch({ type: 'CHANGE_THEME', payload: e.target.value })
|
||||
}}
|
||||
>
|
||||
{playground.themes.map(theme => {
|
||||
return (
|
||||
<option key={theme} value={theme}>
|
||||
{theme}
|
||||
</option>
|
||||
)
|
||||
})}
|
||||
</StyledSelect>
|
||||
</StyledInputLabel>
|
||||
)
|
||||
}
|
||||
|
||||
const ActivePluginOutlet = () => {
|
||||
const [playground] = usePlayground()
|
||||
const plugin = playground.currentPlugin
|
||||
if (!plugin) return null
|
||||
return <StyledPluginOutlet>{plugin.render()}</StyledPluginOutlet>
|
||||
}
|
||||
|
||||
const StyledFooter = styled.footer``
|
||||
|
||||
const StyledFooterBanner = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0.75rem 1rem;
|
||||
border-top: 1px solid #eee;
|
||||
border-bottom: 1px solid #eee;
|
||||
`
|
||||
|
||||
const StyledGrid = styled.div`
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
grid-auto-columns: min-content;
|
||||
align-items: center;
|
||||
grid-gap: 1rem;
|
||||
`
|
||||
|
||||
const StyledSelect = styled.select`
|
||||
padding: 0;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
`
|
||||
|
||||
const StyledPluginList = styled.ol`
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
grid-auto-columns: min-content;
|
||||
align-items: center;
|
||||
grid-gap: 1rem;
|
||||
|
||||
> li {
|
||||
display: flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
border-bottom: 2px solid transparent;
|
||||
|
||||
svg {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&[data-is-active='true'] {
|
||||
cursor: pointer;
|
||||
color: rgba(62, 66, 192, 1);
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
bottom: -19px;
|
||||
left: 0;
|
||||
height: 2px;
|
||||
background: rgba(62, 66, 192, 1);
|
||||
box-shadow: inset 0 0 1px 0 rgb(62, 66, 192), 0 0 1px 0 rgba(62, 66, 192, 0.1), 0 0 15px 0 rgba(128, 131, 216, 0.4),
|
||||
0 2px 6px 0 rgba(111, 115, 247, 0.5), 0 2px 2px 0 rgba(104, 68, 207, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const StyledPluginOutlet = styled.div`
|
||||
padding: 1rem;
|
||||
`
|
||||
|
||||
const StyledInputLabel = styled.label`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
> .label {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
`
|
||||
|
||||
const StyledToggleSlider = styled.span`
|
||||
position: relative;
|
||||
height: 18px;
|
||||
width: 40px;
|
||||
|
||||
input {
|
||||
display: none;
|
||||
|
||||
&:checked + .slider {
|
||||
background: #66bb6a;
|
||||
}
|
||||
|
||||
&:checked + .slider::before {
|
||||
transform: translateX(21px);
|
||||
}
|
||||
}
|
||||
|
||||
.slider {
|
||||
position: absolute;
|
||||
background-color: #ccc;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
transition: 200ms;
|
||||
border-radius: 1rem;
|
||||
|
||||
&::before {
|
||||
background-color: #fff;
|
||||
bottom: 0px;
|
||||
content: '';
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
left: 1px;
|
||||
position: absolute;
|
||||
transition: 200ms;
|
||||
border: 1px solid #bbb;
|
||||
border-radius: 100%;
|
||||
}
|
||||
}
|
||||
`
|
|
@ -1,102 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import * as ReactDOM from 'react-dom'
|
||||
|
||||
/**
|
||||
* Renders React children into an iframe.
|
||||
*
|
||||
* TODO: convert to hooks.
|
||||
*/
|
||||
export class IFrameRenderer extends React.Component {
|
||||
node: any
|
||||
_setInitialContent = false
|
||||
_mounted = false
|
||||
_rendered = false
|
||||
|
||||
componentDidMount() {
|
||||
this._mounted = true
|
||||
|
||||
const doc = this.getDoc()
|
||||
if (doc && doc.readyState === 'complete') {
|
||||
this.forceUpdate()
|
||||
} else {
|
||||
this.node.addEventListener('load', this.handleLoad)
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._mounted = false
|
||||
this.node.removeEventListener('load', this.handleLoad)
|
||||
}
|
||||
|
||||
getDoc() {
|
||||
return this.node && this.node.contentDocument
|
||||
}
|
||||
|
||||
getMountTarget() {
|
||||
const doc = this.getDoc()
|
||||
return doc.getElementById('frame-root')
|
||||
}
|
||||
|
||||
handleLoad = () => {
|
||||
this.forceUpdate()
|
||||
}
|
||||
|
||||
renderIFrameContents() {
|
||||
if (!this._mounted) {
|
||||
return null
|
||||
}
|
||||
|
||||
const doc = this.getDoc()
|
||||
if (!doc) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!this._setInitialContent) {
|
||||
const styles = `
|
||||
html, body, #frame-root {
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#frame-root {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
`
|
||||
doc.write(`<!DOCTYPE html><html><head><style>${styles}</style></head><body><div id="frame-root"></div></body></html>`)
|
||||
doc.close()
|
||||
this._setInitialContent = true
|
||||
}
|
||||
|
||||
const mountTarget = this.getMountTarget()
|
||||
const win = doc.defaultView || doc.parentView
|
||||
|
||||
// Do not allow elements to be focused within the iframe
|
||||
win.HTMLElement.prototype.focus = () => {}
|
||||
|
||||
const ctx = { window: win, document: doc }
|
||||
const content = this.props.children(ctx) || null
|
||||
return ReactDOM.createPortal(content, mountTarget)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { children, ...rest } = this.props
|
||||
return (
|
||||
<iframe
|
||||
title="Example Renderer"
|
||||
seamless
|
||||
ref={node => (this.node = node)}
|
||||
style={{
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
border: 'none',
|
||||
}}
|
||||
{...rest}
|
||||
>
|
||||
{this.renderIFrameContents()}
|
||||
</iframe>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
import React from 'react'
|
||||
import { Playground } from '.'
|
||||
import { Global, css } from '@emotion/core'
|
||||
|
||||
export default {
|
||||
title: 'Playground',
|
||||
component: Playground,
|
||||
}
|
||||
|
||||
// TODO: should share this with the main app and other stories.
|
||||
const GlobalStyles = () => (
|
||||
<Global
|
||||
styles={css`
|
||||
body {
|
||||
font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji',
|
||||
'Segoe UI Emoji', 'Segoe UI Symbol';
|
||||
}
|
||||
`}
|
||||
/>
|
||||
)
|
||||
|
||||
export const ToStorybook = () => {
|
||||
const examples = [
|
||||
{
|
||||
title: 'Hello World',
|
||||
description: 'Description for the first example',
|
||||
source: `import * as React from "react"
|
||||
|
||||
export default () => <h1>Hello World!</h1>`,
|
||||
},
|
||||
{
|
||||
title: 'Goodbye World',
|
||||
description: 'Description for the second example',
|
||||
source: `import * as React from "react"
|
||||
|
||||
export default () => <h1>Goodbye World?</h1>`,
|
||||
},
|
||||
{
|
||||
title: 'RTL Example',
|
||||
description: 'Description for the third example',
|
||||
source: `import * as React from "react"
|
||||
|
||||
export default () => (
|
||||
<div style={{
|
||||
display: "grid",
|
||||
gridAutoFlow: "column",
|
||||
gridAutoColumns: "min-content",
|
||||
gridGap: "1rem",
|
||||
}}>
|
||||
<button style={{ padding: "1rem", fontWeight: 800, background: "red", color: "#fff" }}>Cancel</button>
|
||||
<button style={{ padding: "1rem", fontWeight: 800, background: "blue", color: "#fff" }}>Confirm</button>
|
||||
</div>
|
||||
)`,
|
||||
},
|
||||
{
|
||||
title: 'Theme Example',
|
||||
description: 'Description for the third example',
|
||||
source: `import * as React from "react"
|
||||
|
||||
export default ({ theme }) => {
|
||||
const style = {
|
||||
padding: "1rem"
|
||||
}
|
||||
switch (theme) {
|
||||
case "Dark":
|
||||
style.background = "#333"
|
||||
style.color = "#eee"
|
||||
break
|
||||
case "High Contrast":
|
||||
style.background = "#000"
|
||||
style.color = "#fff"
|
||||
break
|
||||
}
|
||||
return <h1 style={style}>Current theme: {theme}</h1>
|
||||
}`,
|
||||
},
|
||||
]
|
||||
const themes = ['Light', 'Dark', 'High Contrast']
|
||||
return (
|
||||
<>
|
||||
<GlobalStyles />
|
||||
<Playground themes={themes} examples={examples} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
ToStorybook.story = {
|
||||
name: 'Basic',
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import styled from '@emotion/styled'
|
||||
import { PlaygroundProvider, IPlayground, IExample } from './context'
|
||||
import { Sidebar } from './Sidebar'
|
||||
import { Viewport } from './Viewport'
|
||||
import { Footer } from './Footer'
|
||||
|
||||
function handleAction(state: IPlayground, action: any): IPlayground {
|
||||
switch (action.type) {
|
||||
case 'TOGGLE_RTL':
|
||||
return { ...state, rtl: !state.rtl }
|
||||
case 'CHANGE_THEME':
|
||||
return { ...state, currentTheme: action.payload }
|
||||
case 'CHANGE_EXAMPLE':
|
||||
return { ...state, currentExample: action.payload }
|
||||
case 'CHANGE_RESOLUTION':
|
||||
return { ...state, resolution: action.payload }
|
||||
case 'CHANGE_ZOOM_LEVEL':
|
||||
return { ...state, zoomLevel: action.payload }
|
||||
case 'CHANGE_CURRENT_EXAMPLE_SOURCE':
|
||||
return { ...state, currentExample: { ...state.currentExample, source: action.payload } }
|
||||
case 'TOGGLE_PLUGIN':
|
||||
return { ...state, currentPlugin: state.currentPlugin === action.payload ? null : action.payload }
|
||||
default:
|
||||
console.warn('Missing handler for action: %s', action.type)
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
export const Playground = ({ examples = [], themes = [] }: { examples: IExample[]; themes: string[] }) => {
|
||||
// TODO: ensure state stays in sync with changes to examples/themes props.
|
||||
const playground = React.useReducer(handleAction, {
|
||||
examples,
|
||||
themes,
|
||||
rtl: false,
|
||||
zoomLevel: 1,
|
||||
resolution: 'Responsive',
|
||||
currentExample: examples[0],
|
||||
currentTheme: themes[0],
|
||||
currentPlugin: null,
|
||||
})
|
||||
|
||||
return (
|
||||
<PlaygroundProvider value={playground}>
|
||||
<StyledPlayground>
|
||||
<StyledPlaygroundBody>
|
||||
<Viewport />
|
||||
<Sidebar />
|
||||
</StyledPlaygroundBody>
|
||||
<Footer />
|
||||
</StyledPlayground>
|
||||
</PlaygroundProvider>
|
||||
)
|
||||
}
|
||||
|
||||
const StyledPlayground = styled.div`
|
||||
border: 1px solid #eee;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
`
|
||||
|
||||
const StyledPlaygroundBody = styled.div`
|
||||
position: relative;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
`
|
|
@ -1,129 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import styled from '@emotion/styled'
|
||||
import { usePlayground } from './context'
|
||||
|
||||
export const Sidebar = () => {
|
||||
const [show, setShow] = React.useState(true)
|
||||
return (
|
||||
<>
|
||||
<StyledSidebar show={show}>
|
||||
<ExampleList />
|
||||
</StyledSidebar>
|
||||
<ToggleSidebar onToggle={() => setShow(!show)} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const ExampleList = () => {
|
||||
const [playground, dispatch] = usePlayground()
|
||||
return (
|
||||
<SidebarPanel header="Examples">
|
||||
<StyledExampleList>
|
||||
{playground.examples.map(example => {
|
||||
const active = example.title === playground.currentExample.title
|
||||
// TODO: should probably render an actual button or anchor inside of li
|
||||
return (
|
||||
<StyledExampleListItem
|
||||
key={example.title}
|
||||
active={active}
|
||||
onClick={() => {
|
||||
dispatch({ type: 'CHANGE_EXAMPLE', payload: example })
|
||||
}}
|
||||
>
|
||||
{example.title}
|
||||
</StyledExampleListItem>
|
||||
)
|
||||
})}
|
||||
</StyledExampleList>
|
||||
</SidebarPanel>
|
||||
)
|
||||
}
|
||||
|
||||
const ToggleSidebar = ({ onToggle }) => {
|
||||
return (
|
||||
<StyledToggleButton title="Toggle sidebar" onClick={onToggle}>
|
||||
<HamburgerIcon />
|
||||
</StyledToggleButton>
|
||||
)
|
||||
}
|
||||
|
||||
const StyledExampleList = styled.ul`
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
`
|
||||
|
||||
const StyledExampleListItem = styled.li<{ active: boolean }>`
|
||||
padding: 0.5rem;
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
border-bottom-color: #eee;
|
||||
font-size: 0.9rem;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
border-color: rgb(62, 66, 192);
|
||||
}
|
||||
|
||||
${props =>
|
||||
props.active && {
|
||||
background: 'rgba(62, 66, 192, 0.035)',
|
||||
color: 'rgba(62, 66, 192, 1)',
|
||||
borderColor: 'rgba(62, 66, 192, 1)',
|
||||
}}
|
||||
`
|
||||
|
||||
const StyledSidebar = styled.aside<{ show: boolean }>`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 225px;
|
||||
margin-left: ${props => (props.show ? 0 : '-226px')};
|
||||
border-left: 1px solid #eee;
|
||||
background: #fff;
|
||||
transition: margin 150ms ease 0s;
|
||||
`
|
||||
|
||||
export const SidebarPanel = ({ header, children }: { children: React.ReactNode; header: React.ReactNode }) => {
|
||||
const [show, setShow] = React.useState(true)
|
||||
return (
|
||||
<StyledSidebarPanel>
|
||||
<StyledSidebarPanelHeader onClick={() => setShow(!show)}>{header}</StyledSidebarPanelHeader>
|
||||
{show && <StyledSidebarPanelContent>{children}</StyledSidebarPanelContent>}
|
||||
</StyledSidebarPanel>
|
||||
)
|
||||
}
|
||||
|
||||
const StyledSidebarPanel = styled.section``
|
||||
|
||||
const StyledSidebarPanelHeader = styled.header`
|
||||
padding: 0.75rem;
|
||||
border-bottom: 1px solid #eee;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
`
|
||||
|
||||
const StyledSidebarPanelContent = styled.div`
|
||||
background: rgb(249, 249, 249);
|
||||
`
|
||||
|
||||
const HamburgerIcon = () => (
|
||||
<span role="img" aria-hidden="true">
|
||||
<svg role="presentation" focusable="false" height="16" width="16" viewBox="8 8 16 16">
|
||||
<path d="M22.49 10.47c0 .14-.05.25-.14.35s-.21.14-.35.14H9c-.14 0-.25-.05-.35-.14s-.14-.21-.14-.35.05-.25.14-.35.21-.14.35-.14h13c.14 0 .25.05.35.14s.14.21.14.35zm0 5c0 .14-.05.25-.14.35s-.21.14-.35.14H9c-.14 0-.25-.05-.35-.14s-.14-.21-.14-.35.05-.25.14-.35.21-.14.35-.14h13c.14 0 .25.05.35.14s.14.21.14.35zm0 5c0 .14-.05.25-.14.35s-.21.14-.35.14H9c-.14 0-.25-.05-.35-.14s-.14-.21-.14-.35.05-.25.14-.35.21-.14.35-.14h13c.14 0 .25.05.35.14s.14.21.14.35z"></path>
|
||||
<path d="M9 11h13c.6 0 1-.4 1-1s-.4-1-1-1H9c-.6 0-1 .4-1 1s.4 1 1 1zm13 8H9c-.6 0-1 .4-1 1s.4 1 1 1h13c.6 0 1-.4 1-1s-.4-1-1-1zm0-5H9c-.6 0-1 .4-1 1s.4 1 1 1h13c.6 0 1-.4 1-1s-.4-1-1-1z"></path>
|
||||
</svg>
|
||||
</span>
|
||||
)
|
||||
|
||||
const StyledToggleButton = styled.button`
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
margin: 15px 15px 0 0;
|
||||
padding: 0;
|
||||
background: none;
|
||||
border: none;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
z-index: 2;
|
||||
`
|
|
@ -1,116 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import styled from '@emotion/styled'
|
||||
import { usePlayground } from './context'
|
||||
import { ExamplePreview } from './ExamplePreview'
|
||||
|
||||
const RESOLUTIONS = [
|
||||
{ label: 'Responsive', value: 'Responsive' },
|
||||
{ label: '320', value: 320 },
|
||||
{ label: '640', value: 640 },
|
||||
{ label: '1024', value: 1024 },
|
||||
]
|
||||
const ZOOM_LEVELS = [
|
||||
{ label: '25%', value: 0.25 },
|
||||
{ label: '50%', value: 0.5 },
|
||||
{ label: '75%', value: 0.75 },
|
||||
{ label: '100%', value: 1 },
|
||||
{ label: '200%', value: 2 },
|
||||
{ label: '300%', value: 3 },
|
||||
]
|
||||
|
||||
export const Viewport = () => {
|
||||
const [playground, dispatch] = usePlayground()
|
||||
return (
|
||||
<StyledViewport>
|
||||
<Grid style={{ zIndex: 1, padding: '1rem' }}>
|
||||
<Select
|
||||
label="Resolution"
|
||||
options={RESOLUTIONS}
|
||||
value={playground.resolution}
|
||||
onChange={(value: any) => dispatch({ type: 'CHANGE_RESOLUTION', payload: value })}
|
||||
/>
|
||||
<Select
|
||||
label="Zoom Level"
|
||||
options={ZOOM_LEVELS}
|
||||
value={playground.zoomLevel}
|
||||
onChange={(value: any) => dispatch({ type: 'CHANGE_ZOOM_LEVEL', payload: value })}
|
||||
/>
|
||||
</Grid>
|
||||
<StyledExamplePreviewContainer>
|
||||
<ExamplePreview example={playground.currentExample} />
|
||||
</StyledExamplePreviewContainer>
|
||||
{playground.currentExample && (
|
||||
<div style={{ position: 'relative', padding: '1rem' }}>
|
||||
<StyledExampleTitle>{playground.currentExample.title}</StyledExampleTitle>
|
||||
<StyledExampleDescription>{playground.currentExample.description}</StyledExampleDescription>
|
||||
</div>
|
||||
)}
|
||||
</StyledViewport>
|
||||
)
|
||||
}
|
||||
|
||||
const Select = ({ label, options, value, onChange }) => {
|
||||
const selected = options.find(opt => opt.value === value)
|
||||
return (
|
||||
<label htmlFor={label} style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<StyledSelect
|
||||
id={label}
|
||||
value={selected.label}
|
||||
onChange={e => {
|
||||
const option = options.find(opt => opt.label === e.target.value)!
|
||||
onChange(option.value)
|
||||
}}
|
||||
>
|
||||
{options.map(opt => {
|
||||
return (
|
||||
<option key={opt.label} value={opt.label}>
|
||||
{opt.label}
|
||||
</option>
|
||||
)
|
||||
})}
|
||||
</StyledSelect>
|
||||
</label>
|
||||
)
|
||||
}
|
||||
|
||||
const StyledViewport = styled.div`
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex: 1 1 0%;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
min-height: 375px;
|
||||
background: rgb(249, 249, 249);
|
||||
`
|
||||
|
||||
const StyledSelect = styled.select`
|
||||
padding: 0;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
`
|
||||
|
||||
// NOTE: this must take up 100% of the viewport height, since the example
|
||||
// may render with a custom background. In that case, that background should
|
||||
// appear behind the viewport's header and footer.
|
||||
const StyledExamplePreviewContainer = styled.div`
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
`
|
||||
|
||||
const StyledExampleTitle = styled.h3`
|
||||
margin: 0;
|
||||
`
|
||||
const StyledExampleDescription = styled.p`
|
||||
margin: 0.5rem 0;
|
||||
`
|
||||
|
||||
const Grid = styled.div`
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
grid-auto-columns: min-content;
|
||||
align-items: center;
|
||||
grid-gap: 7.5px;
|
||||
`
|
|
@ -1,24 +0,0 @@
|
|||
import * as React from 'react'
|
||||
|
||||
const PlaygroundContext = React.createContext<IPlaygroundContext>(null as any)
|
||||
PlaygroundContext.displayName = 'PlaygroundContext'
|
||||
export const PlaygroundProvider = PlaygroundContext.Provider
|
||||
export const usePlayground = () => React.useContext(PlaygroundContext)
|
||||
|
||||
export type IPlaygroundContext = [IPlayground, React.Dispatch<any>]
|
||||
|
||||
export interface IPlayground {
|
||||
examples: IExample[]
|
||||
themes: string[]
|
||||
currentTheme: string
|
||||
currentExample: IExample
|
||||
currentPlugin: any
|
||||
rtl: boolean
|
||||
zoomLevel: number
|
||||
resolution: number | 'Responsive'
|
||||
}
|
||||
export interface IExample {
|
||||
title: string
|
||||
description: string
|
||||
source: string
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { Playground } from './Playground'
|
|
@ -1,63 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import styled from '@emotion/styled'
|
||||
import { usePlayground } from '../context'
|
||||
|
||||
// TODO: monaco is throwing errors on source change. Dig into
|
||||
// react-monaco-editor; potentially fork (it's a very slim wrapper)
|
||||
const CodeEditor = () => {
|
||||
const MonacoEditor = useLazyMonacoEditor()
|
||||
const [playground, dispatch] = usePlayground()
|
||||
if (!MonacoEditor) {
|
||||
return <span>Loading...</span>
|
||||
}
|
||||
|
||||
const example = playground.currentExample
|
||||
return (
|
||||
<MonacoEditor
|
||||
language="typescript"
|
||||
value={example.source}
|
||||
height={300}
|
||||
onChange={(value: string) => {
|
||||
dispatch({ type: 'CHANGE_CURRENT_EXAMPLE_SOURCE', payload: value })
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function useLazyMonacoEditor() {
|
||||
const [monaco, setMonaco] = React.useState()
|
||||
React.useEffect(() => {
|
||||
let cancelled = false
|
||||
|
||||
Promise.all([import('monaco-editor'), import('react-monaco-editor')]).then(([monaco, MonacoEditor]) => {
|
||||
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
|
||||
jsx: monaco.languages.typescript.JsxEmit.React,
|
||||
})
|
||||
if (!cancelled) {
|
||||
setMonaco(() => MonacoEditor.default)
|
||||
}
|
||||
})
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [])
|
||||
return monaco
|
||||
}
|
||||
|
||||
const StyledSVG = styled.svg`
|
||||
fill: currentColor;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
`
|
||||
|
||||
export default {
|
||||
label: 'Code Editor',
|
||||
icon: (
|
||||
<StyledSVG role="presentation" focusable="false" viewBox="8 8 16 16">
|
||||
<path d="M20 20.5a.993.993 0 0 1-.65-.241.997.997 0 0 1-.108-1.409L21.683 16l-2.441-2.849a.999.999 0 1 1 1.517-1.302l3 3.5a1 1 0 0 1 0 1.301l-3 3.5a.993.993 0 0 1-.759.35zM12 20.5a.995.995 0 0 1-.76-.35l-3-3.5a1 1 0 0 1 0-1.301l3-3.5a1 1 0 0 1 1.518 1.302L10.317 16l2.442 2.85A1 1 0 0 1 12 20.5zM14.251 23.5a.5.5 0 0 1-.482-.638l4-14a.499.499 0 1 1 .961.274l-4 14a.498.498 0 0 1-.479.364z"></path>
|
||||
</StyledSVG>
|
||||
),
|
||||
render(props: any) {
|
||||
return <CodeEditor {...props} />
|
||||
},
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import styled from '@emotion/styled'
|
||||
|
||||
const ThemeEditor = () => {
|
||||
return <p>Placeholder</p>
|
||||
}
|
||||
|
||||
const StyledSVG = styled.svg`
|
||||
fill: currentColor;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
`
|
||||
|
||||
export default {
|
||||
label: 'Theme Editor',
|
||||
icon: (
|
||||
<StyledSVG role="presentation" focusable="false" viewBox="8 8 16 16">
|
||||
<path d="M23.5,9C23.2239,9,23,9.2236,23,9.5v2c0,0.2759-0.2244,0.5-0.5,0.5H20h-8H9.5C9.2244,12,9,11.7759,9,11.5v-2 C9,9.2236,8.7761,9,8.5,9S8,9.2236,8,9.5v2C8,12.3271,8.6729,13,9.5,13H11v3.5c0,0.8271,0.6729,1.5,1.5,1.5H13v5.5 c0,0.1802,0.0969,0.3462,0.2537,0.4351C13.3301,23.9785,13.415,24,13.5,24c0.0891,0,0.1782-0.0239,0.2573-0.0713l5-3 C18.908,20.8384,19,20.6758,19,20.5V18h0.5c0.8271,0,1.5-0.6729,1.5-1.5V13h1.5c0.8271,0,1.5-0.6729,1.5-1.5v-2 C24,9.2236,23.7761,9,23.5,9z M20,16.5c0,0.2759-0.2244,0.5-0.5,0.5h-7c-0.2756,0-0.5-0.2241-0.5-0.5V13h8V16.5z"></path>
|
||||
</StyledSVG>
|
||||
),
|
||||
render() {
|
||||
return <ThemeEditor />
|
||||
},
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
import * as React from 'react'
|
||||
export const PageContext = React.createContext<any>({})
|
||||
|
||||
export const usePageContext = () => React.useContext(PageContext)
|
|
@ -1,63 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import { MDXProvider } from '@mdx-js/react'
|
||||
import { Highlight, HighlightInline } from '../Highlight'
|
||||
import { Global, css } from '@emotion/core'
|
||||
|
||||
export const Provider = props => (
|
||||
<MDXProvider
|
||||
components={{
|
||||
code: Highlight,
|
||||
inlineCode: HighlightInline,
|
||||
}}
|
||||
>
|
||||
<Global
|
||||
styles={css`
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
html,
|
||||
body,
|
||||
#gatsby-focus-wrapper,
|
||||
#___gatsby {
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
font-family: 'Segoe UI';
|
||||
}
|
||||
body,
|
||||
ul[class],
|
||||
ol[class],
|
||||
li,
|
||||
figure,
|
||||
figcaption,
|
||||
blockquote,
|
||||
dl,
|
||||
dd {
|
||||
margin: 0;
|
||||
}
|
||||
input,
|
||||
button,
|
||||
textarea,
|
||||
select {
|
||||
font: inherit;
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
}
|
||||
body {
|
||||
font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji',
|
||||
'Segoe UI Emoji', 'Segoe UI Symbol';
|
||||
background: #fff;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
font-weight: 600;
|
||||
}
|
||||
`}
|
||||
/>
|
||||
{props.children}
|
||||
</MDXProvider>
|
||||
)
|
|
@ -1 +0,0 @@
|
|||
export * from './PageContext'
|
|
@ -1,38 +0,0 @@
|
|||
# Storybook
|
||||
|
||||
Building isolated, reusable components is one of our project goals. If you are starting work on a new feature or want to isolate a component for development / debugging, it is recommended to use [Storybook][storybook].
|
||||
|
||||
## Running Storybook
|
||||
|
||||
```sh
|
||||
yarn storybook
|
||||
```
|
||||
|
||||
## Adding a story
|
||||
|
||||
To add a story to Storybook, place a `<Component>.story.tsx` (Example: Button.story.tsx) file in your component directory and `yarn storybook` from the command line.
|
||||
|
||||
## Writing a story
|
||||
|
||||
You can reference the [Storybook documentation][storybookdocs] for an introduction on “Writing Stories”.
|
||||
|
||||
You can find examples of stories in this repository by searching for `.story.tsx` files.
|
||||
|
||||
```jsx
|
||||
import React from 'react'
|
||||
import { BasicPlayground } from '.'
|
||||
|
||||
export default {
|
||||
title: 'Playground',
|
||||
component: Playground,
|
||||
}
|
||||
|
||||
export const ToStorybook = () => <BasicPlayground />
|
||||
|
||||
ToStorybook.story = {
|
||||
name: 'Basic Playground',
|
||||
}
|
||||
```
|
||||
|
||||
[storybook]: https://storybook.js.org/
|
||||
[storybookdocs]: https://storybook.js.org/basics/writing-stories/
|
|
@ -1,303 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import styled from '@emotion/styled'
|
||||
import Fuse from 'fuse.js'
|
||||
import { useKeyPress } from '../../hooks'
|
||||
|
||||
const dummyData = [
|
||||
{
|
||||
title: "Old Man's War",
|
||||
author: {
|
||||
firstName: 'John',
|
||||
lastName: 'Scalzi',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'The Lock Artist',
|
||||
author: {
|
||||
firstName: 'Steve',
|
||||
lastName: 'Hamilton',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'HTML5',
|
||||
author: {
|
||||
firstName: 'Remy',
|
||||
lastName: 'Sharp',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Right Ho Jeeves',
|
||||
author: {
|
||||
firstName: 'P.D',
|
||||
lastName: 'Woodhouse',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'The Code of the Wooster',
|
||||
author: {
|
||||
firstName: 'P.D',
|
||||
lastName: 'Woodhouse',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Thank You Jeeves',
|
||||
author: {
|
||||
firstName: 'P.D',
|
||||
lastName: 'Woodhouse',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'The DaVinci Code',
|
||||
author: {
|
||||
firstName: 'Dan',
|
||||
lastName: 'Brown',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Angels & Demons',
|
||||
author: {
|
||||
firstName: 'Dan',
|
||||
lastName: 'Brown',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'The Silmarillion',
|
||||
author: {
|
||||
firstName: 'J.R.R',
|
||||
lastName: 'Tolkien',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Syrup',
|
||||
author: {
|
||||
firstName: 'Max',
|
||||
lastName: 'Barry',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'The Lost Symbol',
|
||||
author: {
|
||||
firstName: 'Dan',
|
||||
lastName: 'Brown',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'The Book of Lies',
|
||||
author: {
|
||||
firstName: 'Brad',
|
||||
lastName: 'Meltzer',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Lamb',
|
||||
author: {
|
||||
firstName: 'Christopher',
|
||||
lastName: 'Moore',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Fool',
|
||||
author: {
|
||||
firstName: 'Christopher',
|
||||
lastName: 'Moore',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Incompetence',
|
||||
author: {
|
||||
firstName: 'Rob',
|
||||
lastName: 'Grant',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Fat',
|
||||
author: {
|
||||
firstName: 'Rob',
|
||||
lastName: 'Grant',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Colony',
|
||||
author: {
|
||||
firstName: 'Rob',
|
||||
lastName: 'Grant',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Backwards, Red Dwarf',
|
||||
author: {
|
||||
firstName: 'Rob',
|
||||
lastName: 'Grant',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'The Grand Design',
|
||||
author: {
|
||||
firstName: 'Stephen',
|
||||
lastName: 'Hawking',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'The Book of Samson',
|
||||
author: {
|
||||
firstName: 'David',
|
||||
lastName: 'Maine',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'The Preservationist',
|
||||
author: {
|
||||
firstName: 'David',
|
||||
lastName: 'Maine',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Fallen',
|
||||
author: {
|
||||
firstName: 'David',
|
||||
lastName: 'Maine',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Monster 1959',
|
||||
author: {
|
||||
firstName: 'David',
|
||||
lastName: 'Maine',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
const options = {
|
||||
shouldSort: true,
|
||||
threshold: 0.6,
|
||||
location: 0,
|
||||
distance: 100,
|
||||
maxPatternLength: 32,
|
||||
minMatchCharLength: 1,
|
||||
keys: ['title', 'author.firstName'],
|
||||
}
|
||||
const fuse = new Fuse(dummyData, options)
|
||||
|
||||
export const Search = () => {
|
||||
const [query, setQuery] = React.useState('')
|
||||
const [results, setResults] = React.useState()
|
||||
|
||||
const [isEmpty, setEmpty] = React.useState(true)
|
||||
const [isFocused, setFocus] = React.useState(false)
|
||||
|
||||
const inputRef = React.useRef<any>()
|
||||
|
||||
const handleChange = event => {
|
||||
const value = event.target.value
|
||||
setQuery(value)
|
||||
const results = fuse.search(value)
|
||||
setResults(results)
|
||||
results && results.length > 0 ? setEmpty(false) : setEmpty(true)
|
||||
}
|
||||
|
||||
const handleOnFocus = () => {
|
||||
setFocus(true)
|
||||
}
|
||||
|
||||
const handleOnBlur = () => {
|
||||
setFocus(false)
|
||||
}
|
||||
|
||||
const invokeSearch = useKeyPress('/')
|
||||
|
||||
React.useEffect(() => {
|
||||
invokeSearch && inputRef.current.focus()
|
||||
}, [invokeSearch])
|
||||
|
||||
return (
|
||||
<StyledSearch>
|
||||
<Input
|
||||
ref={inputRef}
|
||||
placeholder={isFocused ? '' : 'Search the docs ("/" to focus)'}
|
||||
value={query}
|
||||
onChange={handleChange}
|
||||
onBlur={handleOnBlur}
|
||||
onFocus={handleOnFocus}
|
||||
/>
|
||||
{isFocused && (
|
||||
<PopOver>
|
||||
{isEmpty ? (
|
||||
<ResultsViews>
|
||||
{query.length > 0 ? (
|
||||
<div className="ResultsViews-Empty">Empty state</div>
|
||||
) : (
|
||||
<div className="ResultsViews-ZeroQuery">Zero query</div>
|
||||
)}
|
||||
</ResultsViews>
|
||||
) : (
|
||||
<Results>
|
||||
<ResultsList>
|
||||
{results.map((result: any) => (
|
||||
<ResultsItem>{result.title}</ResultsItem>
|
||||
))}
|
||||
</ResultsList>
|
||||
</Results>
|
||||
)}
|
||||
</PopOver>
|
||||
)}
|
||||
</StyledSearch>
|
||||
)
|
||||
}
|
||||
|
||||
const StyledSearch = styled.div`
|
||||
position: relative;
|
||||
width: 260px;
|
||||
`
|
||||
|
||||
const Input = styled.input`
|
||||
padding: 10px;
|
||||
margin: 8px 0px 0px;
|
||||
height: 32px;
|
||||
width: 100%;
|
||||
border: 0px none;
|
||||
border-radius: 4px;
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
-moz-appearance: none;
|
||||
overflow: hidden;
|
||||
font-size: 14px;
|
||||
`
|
||||
|
||||
const PopOver = styled.div`
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
left: 0px;
|
||||
padding: 20px;
|
||||
height: 300px;
|
||||
width: 100%;
|
||||
overflow: scroll;
|
||||
|
||||
background-color: #fff;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 20px 30px rgba(100, 100, 100, 0.2);
|
||||
`
|
||||
|
||||
const Results = styled.div`
|
||||
display: flex;
|
||||
`
|
||||
|
||||
const ResultsList = styled.ul`
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
`
|
||||
|
||||
const ResultsItem = styled.li`
|
||||
padding: 0px;
|
||||
margin: 0px 0px 12px;
|
||||
list-style: none;
|
||||
`
|
||||
|
||||
const ResultsViews = styled.div`
|
||||
display: flex;
|
||||
|
||||
opacity: 0.5;
|
||||
|
||||
.EmptyState-None {
|
||||
margin: auto;
|
||||
}
|
||||
`
|
|
@ -1 +0,0 @@
|
|||
export { Search } from './Search'
|
|
@ -1,64 +0,0 @@
|
|||
import * as React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Link } from 'gatsby';
|
||||
import { usePageContext } from '../Provider';
|
||||
|
||||
const Menu = props => {
|
||||
const { items, level = 0 } = props;
|
||||
const menuItems = items.map((item, index) => {
|
||||
if (!item.link && !item.items) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.link && item.link.charAt(0) !== '/') {
|
||||
item.link = '/' + item.link;
|
||||
}
|
||||
|
||||
return (
|
||||
<li key={index} style={{ marginLeft: 16 * level + 'px', marginBottom: 4 }}>
|
||||
{item.link ? <Link to={item.link}>{item.name}</Link> : <span> {item.name}</span>}
|
||||
{item.items && <Menu items={item.items} level={level + 1} />}
|
||||
</li>
|
||||
);
|
||||
});
|
||||
return <StyledList>{menuItems}</StyledList>;
|
||||
};
|
||||
|
||||
export const Sidebar = props => {
|
||||
const {
|
||||
pathContext: { toc = [] }
|
||||
} = usePageContext();
|
||||
|
||||
return (
|
||||
<StyledSidebar>
|
||||
<Menu items={toc} />
|
||||
</StyledSidebar>
|
||||
);
|
||||
};
|
||||
const HeaderHeight = 69;
|
||||
const FooterHeight = 162;
|
||||
|
||||
const StyledSidebar = styled.div`
|
||||
width: 300px;
|
||||
padding: 40px;
|
||||
background-color: rgba(0, 0, 0, 0.01);
|
||||
height: calc(100vh - ${HeaderHeight}px - ${FooterHeight}px);
|
||||
flex: none;
|
||||
`;
|
||||
|
||||
const StyledList = styled.ul`
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
|
||||
li {
|
||||
margin: 0px 0px 20px;
|
||||
padding: 0px;
|
||||
list-style: none;
|
||||
|
||||
a {
|
||||
color: #323130;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
`;
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче