Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Anchored region #530

Merged
merged 15 commits into from
May 4, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "Create nimble-anchored-region",
"packageName": "@ni/nimble-components",
"email": "[email protected]",
"dependentChangeType": "patch"
}
1 change: 1 addition & 0 deletions packages/nimble-components/src/all-components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* that are required instead of leveraging this file.
*/

import './anchored-region';
import './breadcrumb';
import './breadcrumb-item';
import './button';
Expand Down
28 changes: 28 additions & 0 deletions packages/nimble-components/src/anchored-region/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {
DesignSystem,
AnchoredRegion as FoundationAnchoredRegion,
anchoredRegionTemplate as template
} from '@microsoft/fast-foundation';
import { styles } from './styles';

declare global {
interface HTMLElementTagNameMap {
'nimble-anchored-region': AnchoredRegion;
}
}

/**
* A nimble-styled anchored region control.
*/
export class AnchoredRegion extends FoundationAnchoredRegion {}

const nimbleAnchoredRegion = AnchoredRegion.compose({
baseName: 'anchored-region',
baseClass: FoundationAnchoredRegion,
template,
styles
});

DesignSystem.getOrCreate()
.withPrefix('nimble')
.register(nimbleAnchoredRegion());
9 changes: 9 additions & 0 deletions packages/nimble-components/src/anchored-region/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { css } from '@microsoft/fast-element';

export const styles = css`
:host {
contain: layout;
display: block;
z-index: 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the select and the drawer are also configuring z-index. Do we know how these are impacting eachother? https://cs.github.com/ni/nimble?q=z-index

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I set this to 1 to match the select, and I tested that it worked correctly when put inside of a drawer. However, I just tested the menu button within a grid, and I realized that I need to increase the z-index because the header row in the grid has a z-index of 200.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm changing this to 1000. I tested that the menu button looked correct in SystemLink when it was in a grid's toolbar, in a grid's context menu, in the header, in a drawer, and on a generic page.

Copy link
Member

@rajsite rajsite May 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is 1000 coming from? Is that because it's one more than the drawer? Or it's arbitrarily higher than the 200 z-index in the grid?

Unfortunately FAST doesn't have a system for managing z-index values we can copy.

Could we either in this PR or a follow-on (if a follow-on we should make an issue to track it) make a global enum of the different z-index states we use in our styles (like low, medium, high or something) and can include some docs on how the value is derived (ie we chose 1000 because it's larger than the external grid we have compatibility with)?

Maybe we can eventually have some tokens exported that external consumers can use to align z-indexes with nimble. (or even better longer term we can use the dialog element, etc to do the layering for us without the need for z-index)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I picked something that was arbitrarily higher than the 200 z-index in the grid. We don't need it to be higher than the 999 set on the drawer.

I'll create an issue for making a global enum of z-indexes and address it in separate PR. I'm not sure I see exactly what your vision is, so I'd rather have that discussion on a different PR and not delay this one being merged.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created issue: #539

}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import type { Meta, Story } from '@storybook/html';
mollykreis marked this conversation as resolved.
Show resolved Hide resolved
import { html, ViewTemplate } from '@microsoft/fast-element';
import {
createMatrix,
sharedMatrixParameters
} from '../../utilities/tests/matrix';
import { createStory } from '../../utilities/tests/storybook';
import { hiddenWrapper } from '../../utilities/tests/hidden';
import '../../all-components';
import {
bodyFont,
bodyFontColor,
borderHoverColor,
applicationBackgroundColor
} from '../../theme-provider/design-tokens';

const metadata: Meta = {
title: 'Tests/AnchoredRegion',
mollykreis marked this conversation as resolved.
Show resolved Hide resolved
parameters: {
...sharedMatrixParameters()
}
};

export default metadata;

const horizontalPositionStates = [
['Start', 'start'],
['End', 'end'],
['Left', 'left'],
['Right', 'right'],
['Center', 'center']
] as const;
type HorizontalPositionState = typeof horizontalPositionStates[number];

const verticalPositionStates = [
['Top', 'top'],
['Bottom', 'bottom'],
['Center', 'center']
] as const;
type VerticalPositionState = typeof verticalPositionStates[number];

const component = (
[horizontalPositionName, horizontalPosition]: HorizontalPositionState,
[verticalPositionName, verticalPosition]: VerticalPositionState
): ViewTemplate => html`<style>
.container {
display: inline-flex;
margin: 100px;
height: 200px;
width: 200px;
}

.anchor {
top: 100px;
left: 300px;
width: 150px;
height: 150px;
font: var(${bodyFont.cssCustomProperty});
color: var(${bodyFontColor.cssCustomProperty});
border: 2px solid var(${borderHoverColor.cssCustomProperty});
background: var(${applicationBackgroundColor.cssCustomProperty});
}

.anchoredRegion {
width: 75px;
height: 75px;
font: var(${bodyFont.cssCustomProperty});
color: var(${bodyFontColor.cssCustomProperty});
border: 2px solid var(${borderHoverColor.cssCustomProperty});
background: var(${applicationBackgroundColor.cssCustomProperty});
}
</style>
<div class="container">
<div
id="${() => `${verticalPosition}_${horizontalPosition}`}"
class="anchor"
>
Anchor element
</div>
<nimble-anchored-region
anchor="${() => `${verticalPosition}_${horizontalPosition}`}"
fixed-placement="true"
auto-update-mode="auto"
vertical-default-position="${() => verticalPosition}"
vertical-positioning-mode="locktodefault"
horizontal-default-position="${() => horizontalPosition}"
horizontal-positioning-mode="locktodefault"
>
<div class="anchoredRegion">
${horizontalPositionName} ${verticalPositionName}
</div>
</nimble-anchored-region>
<div></div>
</div>`;

export const anchoredRegionThemeMatrix: Story = createStory(
createMatrix(component, [horizontalPositionStates, verticalPositionStates])
);

export const hiddenAnchoredRegion: Story = createStory(
hiddenWrapper(
html`<nimble-anchored-region hidden>Hidden Anchored Region</nimble-anchored-regionx>`
)
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {
DesignSystem,
AnchoredRegion as FoundationAnchoredRegion
} from '@microsoft/fast-foundation';
import { AnchoredRegion } from '..';

describe('Anchored Region', () => {
it('should have its tag returned by tagFor(FoundationAnchoredRegion)', () => {
expect(DesignSystem.tagFor(FoundationAnchoredRegion)).toBe(
'nimble-anchored-region'
);
});

it('can construct an element instance', () => {
expect(document.createElement('nimble-anchored-region')).toBeInstanceOf(
AnchoredRegion
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import type { Meta, StoryObj } from '@storybook/html';
import { html } from '@microsoft/fast-element';
import { createUserSelectedThemeStory } from '../../utilities/tests/storybook';
import '../../all-components';
import {
applicationBackgroundColor,
bodyFont,
bodyFontColor,
borderHoverColor
} from '../../theme-provider/design-tokens';

interface AnchoredRegionArgs {
horizontalPosition: string;
verticalPosition: string;
}

const overviewText = `An anchored region is a container component which enables authors to create layouts where the contents of the anchored region can be
mollykreis marked this conversation as resolved.
Show resolved Hide resolved
positioned relative to another "anchor" element. Additionally, the anchored region can react to the available space between the anchor and a parent
mollykreis marked this conversation as resolved.
Show resolved Hide resolved
["viewport"](https://developer.mozilla.org/en-US/docs/Glossary/viewport) element such that the region is placed on the side of the anchor with the most
available space, or even resize itself based on that space.`;

const metadata: Meta<AnchoredRegionArgs> = {
title: 'Anchored Region',
mollykreis marked this conversation as resolved.
Show resolved Hide resolved
parameters: {
docs: {
description: {
component: overviewText
}
}
},
// prettier-ignore
render: createUserSelectedThemeStory(html<AnchoredRegionArgs>`
<style>
.container {
height: 300px;
}

.anchor {
position: absolute;
top: 100px;
left: 300px;
width: 150px;
height: 150px;
font: var(${bodyFont.cssCustomProperty});
color: var(${bodyFontColor.cssCustomProperty});
border: 2px solid var(${borderHoverColor.cssCustomProperty});
background: var(${applicationBackgroundColor.cssCustomProperty});
}

.anchoredRegion {
width: 75px;
height: 75px;
font: var(${bodyFont.cssCustomProperty});
color: var(${bodyFontColor.cssCustomProperty});
border: 2px solid var(${borderHoverColor.cssCustomProperty});
background: var(${applicationBackgroundColor.cssCustomProperty});
}
</style>
<div class="container">
<div id="${x => `${x.verticalPosition}_${x.horizontalPosition}`}" class="anchor">
mollykreis marked this conversation as resolved.
Show resolved Hide resolved
Anchor element
</div>
<nimble-anchored-region
anchor="${x => `${x.verticalPosition}_${x.horizontalPosition}`}"
fixed-placement="true"
auto-update-mode="auto"
vertical-default-position="${x => x.verticalPosition}"
vertical-positioning-mode="${x => (x.verticalPosition === 'unset' ? 'dynamic' : 'locktodefault')}"
horizontal-default-position="${x => x.horizontalPosition}"
horizontal-positioning-mode="${x => (x.horizontalPosition === 'unset' ? 'dynamic' : 'locktodefault')}"
>
<div class="anchoredRegion">
Anchored region
</div>
</nimble-anchored-region>
</div>
`),
argTypes: {
horizontalPosition: {
options: ['start', 'end', 'left', 'right', 'center', 'unset'],
control: { type: 'select' }
},
verticalPosition: {
options: ['top', 'bottom', 'center', 'unset'],
control: { type: 'select' }
}
},
args: {}
};

export default metadata;

export const anchoredRegion: StoryObj<AnchoredRegionArgs> = {
args: {
horizontalPosition: 'start',
mollykreis marked this conversation as resolved.
Show resolved Hide resolved
verticalPosition: 'top'
}
};