# components We use [Stencil.js](https://stenciljs.com/), a [web-component](https://developer.mozilla.org/en-US/docs/Web/Web_Components) compiler that lets us write custom elements and manage them with TypeScript. Unless you're planning on authoring your own components, this section probably won't be all that interesting to you -- but if you are, great! You've come to the right place. ## Start with the docs [They're right here](https://stenciljs.com/docs/introduction). There isn't much, and a brisk read through them will help you get oriented quickly. If you're familiar with React or Angular, Stencil components operate similarly: values are passed into components as HTML attributes (which Stencil calls "props"), and events are handled using native DOM events and propagation. Encapsulation, composition, use the platform, etc. ## Redux We also use Stencil's built-in [support for Redux](https://stenciljs.com/docs/stencil-redux) for managing state, both in-page and between pageviews by serializing to local storage. Together, these two tools let us manage the DOM more declaratively, treating the Redux container as the single source of truth. Install the [devtools extension](https://github.com/zalmoxisus/redux-devtools-extension) for additional fun and occasional profit. ## Creating a new component Please _do not_ simply copy and paste an existing component to create a new one -- that's what generators are for. Stencil has a lovely component generator you can invoke simply by running: ``` yarn run generate ``` ... and following the prompts. For the tag name, please use the convention `pulumi-somenoun`, and Stencil.js will take care of the rest. By default, you'll be offered the option of creating a stylesheet and both unit and end-to-end tests. Accept all, then change the file extension of the CSS file to `.scss`. ## Developing and testing your component Stencil ships with a development server that runs on port 3333. (It runs independently of the Hugo development server.) To use it, from the project root, run: ``` make serve_components ``` ... and browse to http://localhost:3333. There, you'll see a single HTML page (whose source you'll find at `/components/src/index.html`) that you can use as a sandbox for developing and testing your components in isolation. Any changes you make to that page will be reflected immediately, and changes to the components themselves will trigger a recompile and be copied into Hugo site at `/static/js/`. So if you're running `make serve` in one shell and `make serve_components` in another, saving a component will trigger a reload of the Hugo site as well. (I chose to keep these as separate targets simply because most users who run `make serve` won't need be able to work on components concurrently.) ## Styling components Stencil supports both Sass and the [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM). However, because most of our styling is handled with [TailwindCSS](https://tailwindcss.com/) and customizations, we opt out of Shadow DOM and simply style the components externally, using the Sass defined in `/assets/sass/components`. Other than behavior fundamental to your component (e.g., visibility), most of the styling you apply -- color, type, padding, and so on -- should happen there, and not be expressed as raw CSS alongside your component definition. ## Available components ### pulumi-chooser This component creates a set of tabs for selecting content, like a language, OS, or cloud provider. It can operate either as a self-closing component with no children or as a container with one or more `pulumi-choosable` components as its children. In both cases, by default, the `options` you provide will be set globally and persist between pageviews. Available chooser types are: - `cloud`, with supported options `aws`, `azure`, and `gcp` - `os`, with supported options `macos`, `linux` and `windows` - `language`, with supported options `javascript`, `typescript`, `python`, `go`, `csharp`, `fsharp`, and `visualbasic`. - `k8s-language`, with supported options `typescript`, `typescript-kx`, and `yaml` Options will be ordered automatically by the component; variable sort order is not exposed in the component API. If you want to present a chooser that works independently of what's globally stored, use the `mode="local"` attribute, as indicated in the example below. When a chooser's options don't line up with what's stored globally -- e.g., if a user's default language is C#, but the chooser doesn't have a C# option -- the chooser switches into local mode and displays the first available tab and `pulumi-choosable`, in order to ensure something always appears on the page. #### Usage This chooser sets the selected `os` to persist between pages. ``` ``` This chooser operates in local mode, meaning it ignores whatever happens to be set on the global store. Useful if you want to present content that may be deliberately different from content more relevant to the user's general preferences. ``` Some Mac Stuff Some Windows Stuff Some Linux Stuff ``` This chooser groups a set of options, but hides its controls by providing an `option-style` (which is `tabbed` by default): ``` Some Mac Stuff Some Windows Stuff Some Linux Stuff ``` This is useful if you have a set of related items you want to toggle based on a value on the global store (e.g., language-specific types, filenames and the like). ### pulumi-choosable This component can be supplied either as a child of a chooser, as indicated above, or on its own, in which case it'll simply honor whatever's in the global store. #### Usage ```
I'll only appear when the user's preferred OS is Mac. Otherwise, you'll never see me. ... ``` #### Shortcodes (and some gotchas) In Markdown files, you can express these choosers either as HTML alone or with [Hugo shortcodes](https://gohugo.io/content-management/shortcodes/). For example: ``` {{< chooser language "typescript,go,csharp" >}} ``` .. will render: ```
``` Similarly: ``` {{< chooser cloud "aws,azure,gcp" >}} {{% choosable cloud aws %}} Some AWS stuff. {{% /choosable %}} {{% choosable cloud azure %}} Some Azure stuff. {{% /choosable %}} {{% choosable cloud gcp %}} Some GCP stuff. {{% /choosable %}} {{< /chooser >}} ``` .. will render: ```
Some AWS stuff.
Some Azure stuff.
Some GCP stuff.
``` A few things to note: - Pay attention to how you nest `chooser` and `choosable` shortcodes. [Hugo shortcodes](https://gohugo.io/content-management/shortcodes/) work with both `<>` and `%%` delimiters, but the two behave very differently: `%` will cause content to be Markdown-rendered, `<` and `>` will not. So for example: ``` {{< some-shortcode >}} This content will *not* be rendered as Markdown. {{< /some-shortcode >}} {{% some-shortcode %}} This content *will* be. {{% /some-shortcode %}} ``` You need to be careful about double-rendering, though. For example, this is okay: the outer container won't be rendered as Markdown, but the inner children will be. ``` {{< some-parent >}} Just some regular HTML, here. {{% some-child %}} [Some Markdown]("http://some-link"). {{% /some-child %}} {% some-child %}} [Some more Markdown]("http://some-link"). {{% /some-child %}} {{< /some-parent >}} ``` If you did this, however: ``` {{% some-parent %}} Just some regular HTML, here. {{% some-child %}} [Some Markdown]("http://some-link"). {{% /some-child %}} {% some-child %}} [Some more Markdown]("http://some-link"). {{% /some-child %}} {{% /some-parent %}} ``` ... the nested shortcodes would be rendered twice -- first by virtue of their being wrapped in a `%`-delimited shortcode, and then a second time when the parent's content is rendered itself. Sometimes this turns out okay, but if you're rendering source code, where indentation matters, you can wind up with some terrible results. So as a rule, avoid nesting `%`-delimited shortcodes. Decide which shortcode will be responsible for rendering, and use `%`s on that one, `<>`s on descendants. - The containing `div`s emitted by the `chooser` and `choosable` shortcodes are intentional and in most cases required, because the template renderers we're using today don't know how to handle [custom elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) like these, so wrapping them in a standard HTML tag is necessary. (It's not necessary in HTML layouts files, though -- only in Markdown files.) - By default, active choosables will inherit `display: block;`, but you can override this behavior by passing the [TailwindCSS](https://tailwindcss.com/) utility class `inline` in the chooser (typically in conjunction with `option-style="none"`: ```

It looks like you're on a Mac Windows Linux computer. Nice!

``` ### pulumi-tooltip This component shows a tooltip bubble over its children on mouseover (or touch). The content of the bubble is specified using the `content` [slot](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot). The component also exposes `show()` and `hide()` methods for programmatic control over the bubble's visibility. #### Usage ##### HTML ``` You hovered over the icon! ``` ##### JavaScript ``` You called the show() method! ``` ## Questions? Blame Christian. Also, he's happy to help.