diff --git a/packages/outline-docs/src/guides/99-additional-resources.mdx b/packages/outline-docs/src/guides/99-additional-resources.mdx new file mode 100644 index 000000000..a40364176 --- /dev/null +++ b/packages/outline-docs/src/guides/99-additional-resources.mdx @@ -0,0 +1,50 @@ +import { Meta } from '@storybook/addon-docs'; +import '@phase2/outline-alert'; +import '@phase2/outline-container'; + + + + + ## Documentation Status + + Status: Needs Work/In Progress +

+ This documentation is in need of additional work, or is currently in progress. +

+
+
+ +## Learning Resources & Documentation + +- **Web Components & Tools** + - [Lit Documentation](https://lit.dev/docs) + - [Web Component best practices by Google](https://developers.google.com/web/fundamentals/web-components/best-practices) + - [Web Components ](https://www.webcomponents.org/) +- **Storybook & Stories** + - [Storybook for Web Components Documentation](https://storybook.js.org/docs/web-components/get-started/introduction) + - [Storybook MDX](https://storybook.js.org/docs/web-components/writing-docs/mdx) + - [MDX Documentation](https://mdxjs.com/) +- **JavaScript & TypeScript** + - [ES6 JavaScript](https://www.w3schools.com/js/js_es6.asp) + - [TypeScript Documentation](https://www.typescriptlang.org/docs/) +- **CSS & Styling** + - [CSS Custom Properties](https://developer.mozilla.org/en-US/docs/Web/CSS/--*) + - [Tailwind CSS Documentation](https://tailwindcss.com/docs) + - [CSS-Tricks](https://css-tricks.com/) + +--- diff --git a/packages/outline-docs/src/guides/component-development/01-component-structure.mdx b/packages/outline-docs/src/guides/component-development/01-component-structure.mdx new file mode 100644 index 000000000..755efe659 --- /dev/null +++ b/packages/outline-docs/src/guides/component-development/01-component-structure.mdx @@ -0,0 +1,140 @@ +import { Meta } from '@storybook/addon-docs'; +import '@phase2/outline-button'; +import '@phase2/outline-alert'; +import '@phase2/outline-container'; +import '@phase2/outline-icon'; + + + +# Component Composition + + + ### Documentation Status + + Status: Complete / Up to date (Last Updated: May 2023) + + + +> [Component composition](https://lit.dev/docs/composition/component-composition/) is the process of assembling complex components from simpler components. + +A component can use subcomponents in its template. +Components can use standard DOM mechanisms to communicate: setting properties on subcomponents, and listening for events from subcomponents. + +## Component Structure Overview +Everything used in the component examples below are part of the [lit.dev](https://lit.dev/) library except for the componentStyles include(more info noted below). The following is a brief overview of the main parts of a component. For more information, please see the [Lit](https://lit.dev/docs/) documentation. + +componentStyles is a custom stylesheet that is imported into the component. This is where you would put any custom styles for your component. This is not required, but is recommended. +We import lit.ts compiled version of a PostCSS file, which allows using PostCSS files. + +## Creating a New Component +#### 💡 Outline Specific Note: A new, custom, project-specific component should always extend OutlineElement and retain the Outline namespace (omitting core). (This differs from prior versions where we have used project names as the namespaces) +From the source above on new components: + +>When deciding how to break up functionality, there are several things that help identify when to make a new component. A piece of UI may be a good candidate for a component if one or more of the following applies: +>- It has its own state. +>- It has its own template. +>- It's used in more than one place, either in this component or in multiple components. +>- It focuses on doing one thing well. +>- It has a well-defined API. +> +>Reusable controls like buttons, checkboxes, and input fields can make great components. But more complex UI pieces like drawers and carousels are also great candidates for componentization. + +### Creating a basic New Component +```typescript +import { CSSResultGroup, TemplateResult, html } from 'lit'; +import { customElement } from 'lit/decorators.js'; +import { OutlineElement } from '@phase2/outline-core'; +import componentStyles from './outline-widget.css.lit'; + +/** + * The Outline Widget component + * @element outline-widget + * @slot default - The default slot + */ + +@customElement('outline-widget') +export class OutlineWidget extends OutlineElement { + static styles: CSSResultGroup = [componentStyles]; + + render(): TemplateResult { + return html`Hello World!`; + } +} +``` +## Extending Components +### Extending a component (alias only) +Anytime you need to use a base component, you must extend it. Base components are always named 'outline-core-my-component' by default. The extended name should simply be 'outline-my-component'. This example is the most basic form of extending which just replaces the base styles with any custom project styles you may have. Note: You will need to create a new stylesheet in your extended component directory. + +```typescript +import { OutlineCoreButton } from '@phase2/outline-core-button'; +import componentStyles from './outline-button.css.lit'; + +/** + * The Outline Button component + * @element outline-button + * @slot default - The default slot + */ + +@customElement('outline-button') +export class OutlineButton extends OutlineCoreButton { + static styles: CSSResultGroup = [componentStyles]; +} +``` + +### Extending a component (alias, property, and render) +Here we extend a component just like we did in the last example, but we add a property and change the render function to reflect that new property, adding a class so that styles can be updated based on the passed property. +```typescript +import { OutlineCoreButton } from '@phase2/outline-core-button'; +import componentStyles from './outline-button.css.lit'; + +/** + * The Outline Button component + * @element outline-button + * @attr {string} button-size - The button size. One of: small, medium, large. + * @slot default - The default slot + */ + +export type ButtonSize = 'small' | 'medium' | 'large'; + +@customElement('outline-button') +export class OutlineButton extends OutlineCoreButton { + static styles: CSSResultGroup = [componentStyles]; + + /** + * The button size to use. + */ + @property({ type: String, reflect: true, attribute: 'button-size' }) + buttonSize: ButtonSize = 'medium'; + + render(): TemplateResult { + return html` + + `; + } +} +``` + +## Rendering the Components +To render these components in a story or any consumer (Drupal for example) you would simply do the following (Note we only use button as the other examples are not real and are just for documentation purposes): +```html +This is a button +``` +Renders as: +This is a button \ No newline at end of file diff --git a/packages/outline-docs/src/guides/component-development/03-styles.mdx b/packages/outline-docs/src/guides/component-development/03-styles.mdx new file mode 100644 index 000000000..7f66e4fe8 --- /dev/null +++ b/packages/outline-docs/src/guides/component-development/03-styles.mdx @@ -0,0 +1,334 @@ +import { Meta } from '@storybook/addon-docs'; +import '@phase2/outline-alert'; +import '@phase2/outline-container'; + + + + + ## Description + This section will provide guidance on the specific usages and patterns for styling a component in Outline. + + ## Documentation Status + + Status: Complete / Up to date (Last Updated: May 2023) + + + +## Implementation Standards + +- `styles` should be defined immediately following the class definition. + +## Importing Styles + +The styles for Outline components are kept in a typical `.css` file such as `outline-widget.css` that lives parallel to the +component `outline-widget.ts`. These component CSS files utilize [PostCSS](https://postcss.org/) processing and a variety of plugins to handle various +features including implementing [Tailwind CSS](https://tailwindcss.com/) utility styles to keep the code clean and consistent. + +Lit utilizes the static `styles` to contain any CSS required for a component. + +The following examples shows us using import to bring in styles from our `outline-widget.css.lit.ts` file. Any CSS that is included should be wrapped in Lit’s `css` template literal. +This is where Outline applies additional logic to handle taking a standard CSS file like `outline-widget.css`, +and converts to `outline-widget.css.lit.ts` which is then imported into the main component file, `outline-widget.ts` as seen in the following code sample. + +```typescript +import componentStyles from './outline-widget.css.lit'; +... + +@customElement('outline-widget') +export class OutlineWidget extends OutlineElement { + static styles: CSSResultGroup = [componentStyles]; + ... +} +``` + +## Inheriting Styles + +Interestingly, the above sample is a component, `OutlineWidget`, that extends `OutlineElement`. +This component is simply declaring the styles in `OutlineWidget`, and assumes zero style inheritance from the parent component. +Now, assume that `OutlineElement` provides styles that either should or could be inherited by any component that extends it. If we want to include styles from the parent component, we need to [inherit styles from the superclass](https://lit.dev/docs/components/styles/#inheriting-styles-from-a-superclass). + +```typescript +import componentStyles from './outline-widget.css.lit'; +... + +@customElement('outline-widget') +export class OutlineWidget extends OutlineElement { + static styles: CSSResultGroup = [ OutlineElement.styles, componentStyles ]; + ... +} +``` + +## Inline Styles + +The following example shows both importing content from the default `outline-widget.css.lit.ts` file as well as including inline css wrapped in the `css` string literal provided by the `lit` package. + +```typescript +import { css } from 'lit'; +import componentStyles from './outline-widget.css.lit'; + +@customElement('outline-widget') +export class OutlineWidget extends OutlineElement { + static styles: CSSResultGroup = [ + componentStyles, + css\` + :host { display: block; } + p { margin: 0; } + h2 { color: ${this.color} } + \` + ]; + ... +} +``` + +Above, we can see above, the `${this.color}` variable is being used to dynamically +set the color of the `h2` element. + +> The same effect can be accomplished in the `outline-widgets.css` file by conditionally +> applying custom classes to markup in the `render` method that can be styled with standard CSS. + +## Styling with Tailwind CSS + +Our components are built with the CSS architecture decoupled from the component functionality. +The component markup never utilizes the Tailwind utility classes that are available, +instead relying on the `.css` file itself to use the Tailwind `@apply` directive to attach +styles to a class or element. +This allows us to use as much (or little) of the Tailwind CSS structure as we want, +and have the ability to transition to other CSS frameworks or methods quickly and easily. + +Below is an example of Tailwind classes from our `outline-link.css` file. +Notice how the `@apply` directive precedes a list of shorthand Tailwind classes. +Also note the [`:host`](https://developer.mozilla.org/en-US/docs/Web/CSS/:host) and [`::slotted`](https://developer.mozilla.org/en-US/docs/Web/CSS/::slotted) selectors. These are very helpful when styling components. + +```css + a { + @apply font-body transition-colors duration-300; +} +``` + +[Tailwind CSS documentation](https://tailwindcss.com/docs) is some of the best in the industry to find particular utility classes you may be looking to utililze in your styles. +Tailwind documentation should be referenced at every turn in order to understand the varying impact each utility class may have, and which ones are available. + +## Styling with CSS Variables + +Below is an example of CSS Custom Property usage from our `outline-link.css` file. +In this instance, we have utilized 3 custom CSS variables to help us alter the color of a link when using the `outline-link` component. +The next section will discuss how and where those are declared or overwritten. + +```css +a { + color: var(--outline-link-color-default); +} +``` + +## CSS Variable Declaration + +### Global project variables + +Outline defines all of its CSS Variables in `outline.theme.css`. Many of these CSS variables are utilized by Tailwind CSS as can be seen in the `tailwind.config.js` file. +This means that if you use Tailwind utility classes, they are using the CSS Variables already associated with the design system, and our design tokens. +This ensures that you can adhere to brand guidelines and usage recommendations regardless of how you may choose to architect the CSS or a component or project. + +At a project level, each consumer imports this `outline.theme.css` file globally. +This includes Storybook, our simple HTML consumer development environment, as well as Drupal. + +Any project should consider adding a project specific CSS Variables file to overwrite only specific properties from Outline, without overriding the variables in `outline.theme.css`. +Again, each consumer would need to include this file, immediately following the inclusion of `outline.theme.css`. +However, this separation is only a suggestion, and in theory the `outline.theme.css` file should be safe to be edited much like the `outline.config.js` system configuration. + +```css +:root { + ... + /* Brand specific primary colors. */ + --blue-darken-1: #002536; + --blue-darken-2: #00374e; + --blue-main: #004e70; + --blue-lighten-1: #38758f; + --blue-lighten-2: #9ebcc9; + --blue-lighten-3: #ccdce2; + --blue-lighten-4: #e0eaee; + --blue-lighten-5: #f0f4f6; + ... + /* Configuration values for outline-link. */ + --outline-link-color-default: var(--blue-darken-1); + --outline-link-color-hover: var(--blue-lighten-1); + --outline-link-color-focus: var(--blue-lighten-1); + ... +} +``` + +Note in the above example, the declaration of the `--blue-responsible` color set, +as well as the configuration for the `outline-link` component. +The first set declares the color values as the hex values, +and the next section associates link colors with a pre-existing CSS variable. + +### Component-specific variables + +When a component uses a CSS variable, it should be defined within that component and given a fallback value. +Variables are normally defined within a `vars-COMPONENT.css` file within a `css-variables` subfolder of the component source. +However, using the `css-variables` folder is no longer required. + +Component variables must be defined within a `:host` selector. When defining a variable that a project can override globally, the following naming convention should be used: + +```css +:host { + --COMPONENT--[VARIANT]-[ELEMENT]-SELECTOR--computed: var(--COMPONENT--[VARIANT]-[ELEMENT]-SELECTOR, FALLBACK); +} +``` +where +* `COMPONENT` is the name of the component. +* `VARIANT` is the optional component variant, such as "primary", "secondary", etc. +* `ELEMENT` is the optional HTML element, such as `input` or `h2`, etc +* `SELECTOR` is the css property selector, with special characters replaced by a single hyphen, such as `color` or `bg-color` or `border-cover-hover` . +* * States such as `::hover`, `::active` become `-hover` and `-active`. +* * Selectors such as `input[disabled] border-color` become `input-disabled-border-color` for example, where `input` might be optional if referencing multiple elements (input, textarea, select, etc). +* * Synonyms for properties such as using `bg` for `background`, `weight` or `fw` for `font-weight`, `radius` for `border-radius` etc, are allowed when the intent is obvious. +But avoid when confusing such as with colors (border, background, etc). `color` refers to *text* color since that is the normal CSS property name. See the variable names in Tailwind CSS for other examples. (might update this ADR in the future with an exact list of allowed abbreviations) + +* The `--COMPONENT--[VARIANT]-[ELEMENT]-SELECTOR` variable is the value specified in the `outline.theme.css` or project-specific globals file. +* The `--COMPONENT--[VARIANT]-[ELEMENT]-SELECTOR--computed` is the variable to actually use in your `COMPONENT.css` file. +* The `FALLBACK` is the default value of the variable, either as a hardcoded css value, or a `var(--VARNAME)` to some other variable defined globally. +You must ensure that the FALLBACK value is always defined or the browser will treat rules using this variable as invalid css. + +NOTE: The reason for the `--computed` suffix is because CSS variables cannot redefine themselves. And the above naming convention keeps the fallback +value in a single location in the variable definition rather than being used multiple times within the `COMPONENT.css` file itself. + +Outline components should always declare all component-specific variables using the guidance above. +Project-specific components are encouraged to declare all variables but if a variable is only used in a single place within the `COMPONENT.css` then +using the `--COMPONENT--[VARIANT]-[ELEMENT]-SELECTOR` global variable without making a `--computed` version is allowed as long as the +variable always has a global value or fallback is specified. + +For example: +```css +:host { + --outline-link--color--computed: var(--outline-link--color, var(--primary-color)); +} + +a { + color: var(--outline-link--color--computed) +} +``` + +## Component Style Generation + +#### TODO: Migrate this to Tooling Docs #### +We have talked in previous sections about how the `.css` file will get compiled to a `.css.lit.ts` file. +Let's take a look at a sample of this in action. We will use the `outline-link` component again. + +In this example, we see the combination of the samples we used above, including both Tailwind CSS classes and custom CSS Variables for styling. + +### `outline-widget.css` + +```css +:host, a, ::slotted(a) { + color: var(--outline-link-color-default); +} +``` + +### `outline-widget.css.lit.ts` + +> The following generated file is ignored by default in `.gitignore`, and is generated during all build procedures. + +```typescript +import { css } from 'lit'; +export default css` +/* Apply standardized box sizing to the component. */ +:host { + box-sizing: border-box; +} +:host *, +:host *::before, +:host *::after { + box-sizing: inherit; +} +/* Apply proper CSS for accessibly hiding elements to each component. */ +.visually-hidden { + position: absolute !important; + overflow: hidden; + clip: rect(1px, 1px, 1px, 1px); + width: 1px; + height: 1px; + word-wrap: normal; +} +/* Apply component specific CSS */ +:host, +a, +::slotted(a){ + color:var(--outline-link--color); +} +`; +``` + +The above `.ts` file is now what we include directly to our `outline-link.ts` component, +and apply to the `static styles` array. It includes an initial section, applied to all Outline components, +as well as the second sections which are the styles defined by the `.css` file above. + +## Styling Slots + +CSS styles from external files will have priority over CSS styles that are done via the `:host` selector. +Normally you can only target top-level elements in a slot with css (example `::slotted(ul)`). + +Since we often use Drupal as a consumer, we cannot control the markup that enters slots. The solutions listed below are based on this premise and allow us to style slot content in a similar way to how we style the component. + +When you need to style deeper elements within a slot, there are two approaches: + +1. Copy the slot content into the Shadow DOM via a SlotController. For more information on this, see [Slots](/docs/documentation-guides-component-development-coding-guide-standards-slots--documentation) +2. The slot content can be styled via "global" CSS rules. + +## LightDomStyles Controller ## + +Scoped CSS rules can be created in a number of ways: +* **Added to the consumer's global style sheet.** However, you cannot `@import` an existing stylesheet within a scoping selector such as the component name, making it difficult to reuse existing styles in the design system without duplicating styling. + +* **Scoped at build time.** Outline supports creating a file named `SCOPE.global.css` where `SCOPE` is the scope selector +you wish to add. For example, `my-component.global.css` will wrap the css rules with the `my-component {}` selector. +The build scripts convert this file to `SCOPE.global.scoped.css` and `SCOPE.global.scoped.css.lit.ts` which can be +imported from another css file, or within your component.ts file. This scoped css can be injected into the +global stylesheet using the `LightDomStyles` controller as discussed below. This method increases the size of +the design system JS file to include the scoped css. +> An example of adding light DOM scoped at build time is the `outline-form` component. + +* **Scoped at run time.** The `LightDomStyles` controller mentioned below can automatically scope css rules as they are +added to the global stylesheet. This method does not increase the size of the design system, but adds a small +amount of javascript processing time when the component is rendered. + +### Using `LightDomStyles` to inject global styles +Rather than relying on the consumer to add global styles, the component itself can inject styles into the global stylesheet +using the `LightDomStyles` controller. This controller accepts string of CSS and an optional "scope" selector +and creates a `style` element in the `body` element of the page containing the scoped CSS. + +Example using styles scoped at build time (`*.global.css` files): +``` +import globalStyles from './my-component.global.scoped.css.lit'; +import { LightDomStyles } from '@phase2/outline-core'; +... + lightDomStyles = new LightDomStyles(this, [globalStyles]); +``` + +Example using styles scoped at run time: +``` +import componentStyles from './my-component.css.lit'; +import { LightDomStyles } from '@phase2/outline-core'; +... + lightDomStyles = new LightDomStyles(this, [componentStyles], this.tagName.toLowerCase()); +``` + +> NOTE: When using build-time scoping, you can scope using a class instead of the component name if needed by naming the file `.CLASS.global.css` + +> NOTE: Light DOM styles *can* be overridden by the consumer and CSS rules with a higher precedence can also override the basic scoping. You can handle this in the component by creating more specific CSS selectors if you don't have control over the consumer styling causing the conflict. + +> NOTE: You can freely mix light DOM styling with normal component styling. For example if styling a `ul/li` list, the `ul` style can be within the component normally using `::slotted(ul)` and the style for the `li` can be added to a `my-component.global.css` file and injected via `lightDomStyles`. This helps minimize the bleeding of consumer styles into the component. diff --git a/packages/outline-docs/src/guides/component-development/04-controllers.mdx b/packages/outline-docs/src/guides/component-development/04-controllers.mdx new file mode 100644 index 000000000..cbb24cd01 --- /dev/null +++ b/packages/outline-docs/src/guides/component-development/04-controllers.mdx @@ -0,0 +1,58 @@ +import { Meta } from '@storybook/addon-docs'; +import '@phase2/outline-alert'; +import '@phase2/outline-container'; + + + + + ## Description + Building web components with Outline and Lit2 is a straightforward process thanks to the many tools and extensive documentation we have at the ready. + The following section will go over some of the basics as well as reference related resources where appropriate to serve as a basis of how things are, and will be built in your Outline design system. + + ## Documentation Status + + Status: Needs Work/In Progress +

+ This documentation is in need of additional work, or is currently in progress. +

+
+
+ +## Implementation Standards + +- [Reactive Controllers](https://lit.dev/docs/composition/controllers/) should be instantiated immediately below the style definition. + +## Reactive Controllers + +```typescript +import { reactiveController } from './reactive-controller'; +... + +/** + * The Outline Widget component + * @element outline-widget + */ + +@customElement('outline-widget') + +export class OutlineWidget extends OutlineElement { + static styles: CSSResultGroup = [componentStyles]; + const reactiveController = new ReactiveController(this); + ... +} +``` diff --git a/packages/outline-docs/src/guides/component-development/05-render.mdx b/packages/outline-docs/src/guides/component-development/05-render.mdx new file mode 100644 index 000000000..c2a7e6504 --- /dev/null +++ b/packages/outline-docs/src/guides/component-development/05-render.mdx @@ -0,0 +1,82 @@ +import { Meta } from '@storybook/addon-docs'; +import '@phase2/outline-alert'; +import '@phase2/outline-container'; + + + + + ## Description + The Lit `render` method is what allows us to compose and return any markup required for our component. + In this simple example, this render function is simply returning hard coded HTML markup. Most web components + we develop would never contain just hard coded HTML, as the components are designed to be flexible and reusable, + which calls for a more dynamic `render` method. Yet, there’s absolutely nothing wrong with returning simple HTML markup exactly as shown below. + + ## Documentation Status + + Status: Needs Work/In Progress +

+ This documentation is in need of additional work, or is currently in progress. +

+
+
+ +## Render Method + +```typescript +... + render(): TemplateResult { + return html` +
+

+ Simple "hard coded" paragraph/element inside the component's + ShadowDOM. +

+
+ `; + } + ... +} +``` +
+ +## Partial Templates + +```typescript +... + render(): TemplateResult { + return html`${headerTemplate}`; + } + + /* Example usage of a partial template, implemented above * in the render() function. */ + + headerTemplate(): TemplateResult { + if (boolProperty) { + return html` +

Hello World

+ `; + } + else { + return html` +

Hola Mundo

+ `; + } + } + ... +} +``` +[Partial Templates](https://lit.dev/docs/components/rendering/#composing-templates) are used to break up the render function into smaller chunks and allows for overriding of pieces of the template in subclasses. This is also great for extracting conditional logic from the render function. diff --git a/packages/outline-docs/src/guides/component-development/06-properties.mdx b/packages/outline-docs/src/guides/component-development/06-properties.mdx new file mode 100644 index 000000000..54d97c5a2 --- /dev/null +++ b/packages/outline-docs/src/guides/component-development/06-properties.mdx @@ -0,0 +1,143 @@ +import { Meta } from '@storybook/addon-docs'; +import '@phase2/outline-alert'; +import '@phase2/outline-container'; + + + + + ## Description + Outline components receive input and store their state as JavaScript class fields(normally called attributes) or properties. + + [Reactive properties](https://lit.dev/docs/components/properties/) are properties that can trigger the reactive update cycle when changed, re-rendering the component, and optionally be read or written to attributes. + + ## Documentation Status + + Status: Complete / Update to data May 2023 +

+ This documentation is in need of additional work, or is currently in progress. +

+
+
+ +### Property usage basics +- Properties should be commented using [JSDoc](https://github.com/jsdoc/jsdoc). +- Each property should provide a comment including a description of the attribute functionality or purpose. +- Pass to `type` the appropriate [converter](https://lit.dev/docs/components/properties/#conversion-type) (`String`, `Boolean`, etc.). +- Pass to `attribute` an attribute name in kebab-case. +- Attribute names must not collide with HTML standard ones `title`, `href`, etc. + - This means it is best to name properties more like `link-title`, `link-href`, etc. +- Lit [documentation](https://lit.dev/docs/components/properties/) + +### Slots versus properties +- When do I use a property? + - Properties and slots should mirror HTML as close as possible. For example, if something would just be a in a div, it should probably be a slot. However, something like an aria-label should typically be a property. + - Properties are used for non-content features that impact the state of the component. Non-content here means any structured data that isn't just HTML. For example, active versus inactive states. + - There are some exceptions, such as making alt-text for images, which might make more sense to be a property. +- The component shouldn't change its own public properties, except in response to user input. For example, you could set a property to change background color, but the component itself shouldn't change it's own color. +- There are definitely cases where a component is doing something internally that warrants reflecting its internal state back in the HTML markup regardless of any user action. For example, detecting dark mode or an a11y config. +- In general slots are used for content, such as HTML markup, text, icons, or images. +- The more of the component that is slots, the more flexible the component will be. +- There are cases in which adding content as a property will make sense, for example using a title to control the state of the component. Use this cautiously, as it can cause issues with a11y tools and automation. This is mostly only with older tools/ low quality tools. + +### Property with default value + +The below sample uses the `stringProperty = ''` to provide a default value to the property if it is not supplied or calculated by the component itself. + +```typescript +@property({ + type: String, + attribute: 'string-property' +}) +stringProperty = 'Default Property Value' +``` + +### Property without default value + +The below sample simply uses the variable name `stringProperty` after the `@property` decorator to not supply a default value, thus making it `NULL` by default. + +```typescript +@property({ + type: String, + attribute: 'string-property' +}) +stringProperty +``` + +## Property Types + +### String + +```typescript +@property({ + type: String, + attribute: 'string-property' +}) +stringProperty = 'Default string value' +``` + +### Number + +```typescript +@property({ + type: Number, + attribute: 'number-property' +}) +numberProperty = 42 +``` + +### Boolean + +Boolean properties are a special case, and should always default to a value of `false`. +This is so that the property can be accurately passed via HTML markup, which only passes a string value which returns as truthy. + +```typescript +@property({ + type: Boolean, + attribute: 'boolean-property' +}) +booleanProperty = false +``` + +In the above example, a usage of this component would look like `` means the value of `booleanProperty` is `true`. +The simple exclusion of the attribute on the HTML element would then represent the default value of `false`. + +### Object + +```typescript +@property({ + type: Object, + attribute: 'object-property' +}) +objectProperty = { + thingOne: 'value', + thingTwo: 'value' +} +``` + +### Array + +```typescript +@property({ + type: Array, + attribute: 'array-property' +}) +arrayProperty = [ + { thing: 'value' }, + { thing: 'value' } +] +``` diff --git a/packages/outline-docs/src/guides/component-development/07-slots.mdx b/packages/outline-docs/src/guides/component-development/07-slots.mdx new file mode 100644 index 000000000..0f67cefc1 --- /dev/null +++ b/packages/outline-docs/src/guides/component-development/07-slots.mdx @@ -0,0 +1,108 @@ +import { Meta } from '@storybook/addon-docs'; +import '@phase2/outline-alert'; +import '@phase2/outline-container'; + + + + + ## Description + Slots allow content to be passed into a web component. + This is useful for allowing the consumer to pass in content that will be rendered inside the component's ShadowDOM. + + ## Documentation Status + + Status: Needs Work/In Progress +

+ This documentation is in need of additional work, or is currently in progress. +

+
+
+ +## Declaring Slots + +[Slots](https://lit.dev/docs/components/shadow-dom/#slots) are a method that allows us to pass markup from the consumer into a placeholder inside the web component. + +### JSDoc for slots + +```typescript +... +/** + * The Outline Widget component + * @element outline-widget + * @slot default slot. + * @slot heading: for content above the default slot. + * @slot footer: for content below the default slot. + */ +... +``` + +### `@customElement` Decorator + +```typescript +... +@customElement('outline-widget') +export class OutlineWidget extends OutlineElement { +... +} +``` + +### `render()` method for slots + +```typescript +... + render(): TemplateResult { + return html` + + + +

"Hard coded" element inside the component's ShadowDOM.

+ ` + } +... +``` + +### Default placeholder content in slots. + +```typescript +... + render(): TemplateResult { + return html` + Default header content + default content for the default slot + default content for the footer slot + ` + } +... +``` +
+ +## Using Slots + +```html + + +

This paragraph is content that will be passed to the default slot in the component.

+
+ + + +

This is passed to the heading slot.

+

This is passed to the default slot.

+

This is passed to the footer slot.

+
+``` diff --git a/packages/outline-docs/src/guides/component-development/08-lifecycle-methods.mdx b/packages/outline-docs/src/guides/component-development/08-lifecycle-methods.mdx new file mode 100644 index 000000000..8f5fff0d1 --- /dev/null +++ b/packages/outline-docs/src/guides/component-development/08-lifecycle-methods.mdx @@ -0,0 +1,78 @@ +import { Meta } from '@storybook/addon-docs'; +import '@phase2/outline-alert'; +import '@phase2/outline-container'; + + + + + ## Description + Lit components use the standard custom element [lifecycle](https://lit.dev/docs/components/lifecycle/) methods. In addition Lit introduces a reactive update cycle that renders changes to DOM when reactive properties change. + + ## Documentation Status + + Status: Needs Work/In Progress +

+ This documentation is in need of additional work, or is currently in progress. +

+
+
+ +## Lifecycle Methods + +```typescript +// Called when an element is created. Also, it’s invoked when an existing +// element is upgraded, which happens when the definition for a custom +// element is loaded after the element is already in the DOM. +constructor() { + super(); +}; + +// Invoked when a component is added to the document's DOM. +connectedCallback() { + super.connectedCallback() +}; + +// Called to determine whether an update cycle is required. +shouldUpdate() {}; + +// Called before update() to compute values needed during the update. +willUpdate() {}; + +// Called after the component's DOM has been updated the first time, +// immediately before updated() is called. +firstUpdated() {}; + +// Called to update the component's DOM. +update(){ + super.update(); +}; + +// Called to update the component's DOM. +updated() {}; + +// Invoked when a component is removed from the document's DOM. +disconnectedCallback() { + super.disconnectedCallback() +}; + +// Called by `update()` and should be implemented to return a renderable result +// (such as a `TemplateResult`) used to render the component's DOM. +render() {}; +``` + +[Lifecycle Methods](https://lit.dev/docs/components/lifecycle/) should be placed in approximate chronological order. The render function is placed at the bottom of these. Note that some lifecycle methods require `super` to be called. diff --git a/packages/outline-docs/src/guides/component-development/09-events.mdx b/packages/outline-docs/src/guides/component-development/09-events.mdx new file mode 100644 index 000000000..4b4c48e42 --- /dev/null +++ b/packages/outline-docs/src/guides/component-development/09-events.mdx @@ -0,0 +1,47 @@ +import { Meta } from '@storybook/addon-docs'; +import '@phase2/outline-alert'; +import '@phase2/outline-container'; + + + + + ## Description + [Events](https://lit.dev/docs/components/events/) are the standard way that elements communicate changes. + These changes typically occur due to user interaction. + For example, a button dispatches a click event when a user clicks on it; + an input dispatches a change event when the user enters a value in it. + + ## Documentation Status + + Status: Needs Work/In Progress +

+ This documentation is in need of additional work, or is currently in progress. +

+
+
+ +## Event Handlers + +```typescript +eventHandler = (e: Event) => { + e.preventDefault() + console.log('eventhandler fired') +} +``` + +Event handlers are placed below the template partials. diff --git a/packages/outline-docs/src/guides/component-development/10-utility-functions.mdx b/packages/outline-docs/src/guides/component-development/10-utility-functions.mdx new file mode 100644 index 000000000..6d5cba4f6 --- /dev/null +++ b/packages/outline-docs/src/guides/component-development/10-utility-functions.mdx @@ -0,0 +1,39 @@ +import { Meta } from '@storybook/addon-docs'; +import '@phase2/outline-alert'; +import '@phase2/outline-container'; + + + + ## Documentation Status + + Status: Needs Work/In Progress +

+ This documentation is in need of additional work, or is currently in progress. +

+
+
+ +## Utility Functions + +```typescript +private utilityFunction = (data: string[]) => { + data.forEach(str: string=> console.log(str)) +} +``` + +Utility functions are placed below the template partials and event handlers. diff --git a/packages/outline-docs/src/guides/component-development/11-extending.mdx b/packages/outline-docs/src/guides/component-development/11-extending.mdx new file mode 100644 index 000000000..908bfee66 --- /dev/null +++ b/packages/outline-docs/src/guides/component-development/11-extending.mdx @@ -0,0 +1,132 @@ +import { Meta } from '@storybook/addon-docs'; +import '@phase2/outline-alert'; +import '@phase2/outline-container'; + + + + + ## Description + This section will provide an in-depth overview of extending components with Lit and Outline. + The abiilty to extend components is one of, if not the core reason we utilize Lit as the standard for Outine component development. + + ## Documentation Status + + Status: Needs Work/In Progress +

+ This documentation is in need of additional work, or is currently in progress. +

+
+
+ +## Best Practices + +Whenever a `.ts` file requires an update, it is recommended to extend that element to a specific element of your design system. +ie. when customizing the button component functionality beyond CSS styling, extend `outline-button` to `acme-button`. + +## Extending Components in Outline + +Lit gives us the ability to extend the base `LitElement` component class as well as our own custom component classes. +In all of the previous examples, including the one below, you will notice the sample `OutlineWidget` +class extends `OutlineElement` instead of `LitElement`. `OutlineElement` +is our custom default class that already extends `LitElement`, and provides +some additional structure and functionality that can be utilized by any component +with `OutlineElement` in its chain. + +```typescript +... +/** + * The Outline Widget component * @element outline-widget + */ +@customElement('outline-widget') +export class OutlineWidget extends OutlineElement { + ... +} +``` + +## Examples + +### Overriding Styles + +```typescript +import componentStyles from './outline-widget.css.lit'; +... +/** + * The Outline Widget component * @element outline-widget + */ +@customElement('outline-widget') +export class OutlineWidget extends OutlineElement { + static styles: CSSResultGroup = [componentStyles]; + ... +} +``` + +The above example, one we have seen previously in the styles section, +already provides an example of overriding an element of a superclass. +This example shows us how to extend a component and override the styles +applied to the new component. + +This scenario is common when the styling for a component is drastically different from +the defaults provided by the original component. Sometimes it is easier to start your +new components CSS from scratch, but retain all the functionality of the component. + +### Overriding Properties + +In this example, we will declare `OutlineWidget`, and create a property, `booleanProperty`, which defaults to `false`. +Then we create an `ExtendingOutlineWidget` component, and override the property by setting `booleanProperty` to `true`. + +#### Class `OutlineWidget` + +```typescript +import { OutlineElement } from '@phase2/outline-core'; +... +/** + * The Outline Widget component * @element outline-widget + */ +@customElement('outline-widget') +export class OutlineWidget extends OutlineElement { + /** + * This is our boolean property in our new component. + */ + @property({ + type: Boolean, + attribute: 'boolean-property' + }) +booleanProperty = false +... +} +``` + +#### Class `ExtendingOutlineWidget` + +```typescript +import { OutlineWidget } from 'path/to/outline-widget'; +... +/** + * The Extending Outline Widget component + * @element extending-outline-widget + */ +@customElement('outline-widget') +export class ExtendingOutlineWidget extends OutlineWidget { + /** + * This is our new component changing the default value of a property. + * It was originaly false inside of OutlineWidget. + */ + booleanProperty = true + ... +} +``` diff --git a/packages/outline-docs/src/guides/component-development/main.mdx b/packages/outline-docs/src/guides/component-development/main.mdx new file mode 100644 index 000000000..305a074d3 --- /dev/null +++ b/packages/outline-docs/src/guides/component-development/main.mdx @@ -0,0 +1,35 @@ +import { Meta } from '@storybook/addon-docs'; +import '@phase2/outline-alert'; +import '@phase2/outline-container'; + + + + + + ## Description + Building web components with Outline and Lit2 is a straightforward process thanks to the many tools and extensive documentation we have at the ready. + The following section will go over some of the basics as well as reference related resources where appropriate to serve as a basis of how things are, and will be built in your Outline design system. + + ## Documentation Status + + Status: Needs Work/In Progress +

+ This documentation is in need of additional work, or is currently in progress. +

+
+
\ No newline at end of file