Skip to content

Commit

Permalink
Merge branch 'master' into users/janechu/add-api-extractor-to-all-exp…
Browse files Browse the repository at this point in the history
…orts
  • Loading branch information
janechu authored Jun 13, 2024
2 parents 34a8347 + 8ba27ed commit f76306d
Show file tree
Hide file tree
Showing 9 changed files with 145 additions and 71 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "removing readonly for Slider",
"packageName": "@microsoft/fast-foundation",
"email": "olaf-k@users.noreply.github.com",
"dependentChangeType": "prerelease"
}
3 changes: 0 additions & 3 deletions packages/web-components/fast-foundation/docs/api-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -1875,9 +1875,6 @@ export class FASTSlider extends FormAssociatedSlider implements SliderConfigurat
protected orientationChanged(): void;
// @internal (undocumented)
position: string;
readOnly: boolean;
// (undocumented)
protected readOnlyChanged(): void;
step: number | undefined;
// (undocumented)
protected stepChanged(): void;
Expand Down
25 changes: 11 additions & 14 deletions packages/web-components/fast-foundation/src/slider/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,24 +166,22 @@ export const mySliderLabel = SliderLabel.compose({

#### Fields

| Name | Privacy | Type | Default | Description | Inherited From |
| -------------------- | --------- | ----------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- |
| `readOnly` | public | `boolean` | | When true, the control will be immutable by user interaction. See [readonly HTML attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/readonly) for more information. | |
| `valueAsNumber` | public | `number` | | The value property, typed as a number. | |
| `valueTextFormatter` | public | `(value: string) => string or null` | | Custom function that generates a string for the component's "aria-valuetext" attribute based on the current value. | |
| `min` | public | `number` | `0` | The minimum allowed value. | |
| `max` | public | `number` | `10` | The maximum allowed value. | |
| `step` | public | `number or undefined` | | Value to increment or decrement via arrow keys, mouse click or drag. | |
| `orientation` | public | `Orientation` | | The orientation of the slider. | |
| `mode` | public | `SliderMode` | | The selection mode. | |
| `keypressHandler` | protected | | | | |
| `proxy` | | | | | FormAssociatedSlider |
| Name | Privacy | Type | Default | Description | Inherited From |
| -------------------- | --------- | ----------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------ | -------------------- |
| `valueAsNumber` | public | `number` | | The value property, typed as a number. | |
| `valueTextFormatter` | public | `(value: string) => string or null` | | Custom function that generates a string for the component's "aria-valuetext" attribute based on the current value. | |
| `min` | public | `number` | `0` | The minimum allowed value. | |
| `max` | public | `number` | `10` | The maximum allowed value. | |
| `step` | public | `number or undefined` | | Value to increment or decrement via arrow keys, mouse click or drag. | |
| `orientation` | public | `Orientation` | | The orientation of the slider. | |
| `mode` | public | `SliderMode` | | The selection mode. | |
| `keypressHandler` | protected | | | | |
| `proxy` | | | | | FormAssociatedSlider |

#### Methods

| Name | Privacy | Description | Parameters | Return | Inherited From |
| -------------------------------- | --------- | ------------------------------------------- | ---------------------- | ------ | -------------- |
| `readOnlyChanged` | protected | | | `void` | |
| `minChanged` | protected | | | `void` | |
| `maxChanged` | protected | | | `void` | |
| `stepChanged` | protected | | | `void` | |
Expand All @@ -202,7 +200,6 @@ export const mySliderLabel = SliderLabel.compose({

| Name | Field | Inherited From |
| ------------- | ----------- | -------------- |
| `readonly` | readOnly | |
| | min | |
| | max | |
| | step | |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,6 @@ test.describe("Slider", () => {
);
});

test("should NOT set a default `aria-readonly` value when `readonly` is not defined", async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fast-slider></fast-slider>
`;
});

await expect(element).not.toHaveAttribute("aria-readonly");
});

test("should initialize to the initial value if no value property is set", async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
Expand Down Expand Up @@ -142,20 +132,6 @@ test.describe("Slider", () => {
await expect(element).not.toHaveAttribute("tabindex", "0");
});

test("should set the `aria-readonly` attribute when `readonly` value is true", async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fast-slider></fast-slider>
`;
});

await element.evaluate((node: FASTSlider) => {
node.readOnly = true;
});

await expect(element).toHaveAttribute("aria-readonly", "true");
});

test("should set the `aria-orientation` attribute equal to the `orientation` value", async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export function sliderTemplate<T extends FASTSlider>(
aria-valuemin="${x => x.min}"
aria-valuemax="${x => x.max}"
aria-disabled="${x => (x.disabled ? true : void 0)}"
aria-readonly="${x => (x.readOnly ? true : void 0)}"
aria-orientation="${x => x.orientation}"
>
<div part="positioning-region" class="positioning-region">
Expand Down
22 changes: 3 additions & 19 deletions packages/web-components/fast-foundation/src/slider/slider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,6 @@ import { SliderMode } from "./slider.options.js";
* @public
*/
export class FASTSlider extends FormAssociatedSlider implements SliderConfiguration {
/**
* When true, the control will be immutable by user interaction.
* See {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/readonly | readonly HTML attribute} for more information.
*
* @public
* @remarks
* HTML Attribute: readonly
*/
@attr({ attribute: "readonly", mode: "boolean" })
public readOnly: boolean; // Map to proxy element
protected readOnlyChanged(): void {
if (this.proxy instanceof HTMLInputElement) {
this.proxy.readOnly = this.readOnly;
}
}

/**
* @internal
*/
Expand Down Expand Up @@ -293,7 +277,7 @@ export class FASTSlider extends FormAssociatedSlider implements SliderConfigurat
}

protected keypressHandler = (e: KeyboardEvent) => {
if (this.readOnly || this.disabled) {
if (this.disabled) {
return;
}

Expand Down Expand Up @@ -437,7 +421,7 @@ export class FASTSlider extends FormAssociatedSlider implements SliderConfigurat
* Handle mouse moves during a thumb drag operation
*/
private handleMouseMove = (e: MouseEvent | TouchEvent): void => {
if (this.readOnly || this.disabled || e.defaultPrevented) {
if (this.disabled || e.defaultPrevented) {
return;
}
// update the value based on current position
Expand Down Expand Up @@ -498,7 +482,7 @@ export class FASTSlider extends FormAssociatedSlider implements SliderConfigurat
*/
private handleMouseDown = (e: MouseEvent | null) => {
const eventAction = `${e !== null ? "add" : "remove"}EventListener`;
if (e === null || (!this.disabled && !this.readOnly)) {
if (e === null || !this.disabled) {
window[eventAction]("mouseup", this.handleWindowMouseUp);
window.document[eventAction]("mouseleave", this.handleWindowMouseUp);
window[eventAction]("mousemove", this.handleMouseMove);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,8 @@ const styles = css`
bottom: 0;
}
:host([disabled]),
:host([readonly]) {
cursor: var(--disabled-cursor);
}
:host([disabled]) {
cursor: var(--disabled-cursor);
opacity: var(--disabled-opacity);
}
`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { SliderMode, SliderOrientation } from "../slider.options.js";
const storyTemplate = html<StoryArgs<FASTSlider>>`
<fast-slider
?disabled="${x => x.disabled}"
?readonly="${x => x.readOnly}"
max="${x => x.max}"
min="${x => x.min}"
mode="${x => x.mode}"
Expand All @@ -24,15 +23,13 @@ export default {
title: "Slider",
args: {
disabled: false,
readOnly: false,
},
argTypes: {
disabled: { control: "boolean" },
max: { control: "number" },
min: { control: "number" },
mode: { control: "radio", options: Object.values(SliderMode) },
orientation: { control: "radio", options: Object.values(SliderOrientation) },
readOnly: { control: "boolean" },
step: { control: "number" },
storyContent: { table: { disable: true } },
storyItems: { control: "object" },
Expand Down
125 changes: 123 additions & 2 deletions sites/website/src/docs/advanced/dependency-injection.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,128 @@ sidebar_label: Dependency Injection
custom_edit_url: https://github.com/microsoft/fast/edit/master/sites/website/src/docs/apps-and-experiences/dependency-injection.md
description: FAST introduces the concept of a dependency injection container.
keywords:
- dependency injection container
- dependency injection
- container
- createContainer
- getOrSetDOMContainer
---

// TODO
## Introduction

Dependency injection is a flexible & modular design pattern for passing functionality to the parts of an app that need it. If you need your project to be more maintainable and testable, this might be a good solution for you to use. The `@microsoft/fast-element` package has dependency injection utilities for regular use in JavaScript, and for injecting dependencies into web components.

### Basic Example

The first step to dependency injection is the creation of a container, this is where dependencies will be injected and resolved. There are two available ways to create a container, you can either use `createContainer()` or `getOrCreateDOMContainer()`. You can have as many containers as you like, however they will only resolve dependencies registered within their container.

```ts
import { DI } from "@microsoft/fast-element/di.js";

const container = DI.createContainer();
```

Now that your container is created, let's create a few dependencies to host from it.

First let's define an interface for the dependency which we'll call `MyServiceConfig`, and create a `ContextDecorator` of the same name which will take the interface as it's generic type:

```ts
import { DI } from "@microsoft/fast-element/di.js";

export interface MyServiceConfig {
get: () => Promise<Response>;
}

export const MyServiceConfig = DI.createContext();
```

Next let's define our implementation of the service:

```ts
export class MyService implements MyServiceConfig {
private serviceUrl: string = "http://localhost:7776";

async get(): Promise<Response> {
return await fetch(this.serviceUrl, { method: "GET" });
}
}
```

Now that we have our service defined, let's add it as a dependency to our application `App` class. Note the use of the `ContextDecorator` injected into the constructor as `@MyServiceConfig`. This will attach it to the instance with the same name.

```ts
export class App implements AppConfig {
constructor(
@MyServiceConfig private readonly myService: MyServiceConfig
) {
return;
}

public async getMyServiceStatus(): Promise<number> {
return (await this.myService.get()).status;
}
}
```

Finally, let's update our container by registering `MyService` with the dependency injection container. We will use the `MyServiceConfig` as the key, this will allow `App` to resolve the dependency. Note the use of `transient`, this is a utility for creating an instance each time the service is fetched. Other utilities are available, refer to the `di` API documentation for details.

```ts
import { DI, Registration } from "@microsoft/fast-element/di.js";

const { transient } = Registration;

const container = DI.createContainer();

container.register(transient(MyServiceConfig, MyService));

const responseStatus = await container.get(App).getMyServiceStatus();

console.log("Server Status:", responseStatus);
```

### Using Dependency Injection with Web Components

#### Register Dependencies Before Defining Web Components

When using dependency injection with web components, ensure you define your container **before** you define your web components. Otherwise your web components may be initialized before the dependencies can be resolved.

#### Use `getOrCreateDOMContainer()`

The `getOrCreateDOMContainer()` must be used for web components to resolve dependencies. Pass in a node to create the container for, in this example we will use `document.body`.

```ts
import { DI, Registration } from "@microsoft/fast-element/di.js";

const { transient } = Registration;

const container = DI.getOrCreateDOMContainer(document.body);

container.register(transient(MyServiceConfig, MyService));
```

#### Access Dependencies in the `connectedCallback`

Once the web component has reached the `connectedCallback` hook, you will be able to access any injected dependencies.

```ts
import { FASTElement, html, observable } from "@microsoft/fast-element";

export class MyComponent extends FASTElement {
@MyServiceConfig myService!: MyServiceConfig;

@observable
status: number;

connectedCallback() {
super.connectedCallback();

this.myService.get().then((value: Response) => {
this.status = value.status;
});
}
}

MyComponent.define({
name: "my-component",
template: html`<div>${x => x.status}</div>`
});
```

0 comments on commit f76306d

Please sign in to comment.