From 275498c52eb12cecf3e36bb163c624a10e5b4aa7 Mon Sep 17 00:00:00 2001 From: Ben Grynhaus Date: Mon, 21 Jan 2019 19:38:16 +0200 Subject: [PATCH] fix dataset not being passed to office-ui-fabric-react in contextual menu items declared in template (directives) --- apps/demo/src/app/app.component.html | 16 ++++++---- .../button/base-button.component.ts | 2 ++ .../directives/command-bar-item.directives.ts | 6 +++- .../command-bar-items.directives.ts | 4 ++- .../contextual-menu-item.directive.ts | 12 +++++++ .../src/lib/utils/get-data-attributes.ts | 32 +++++++++++++++++++ libs/fabric/src/lib/utils/kebab-case.ts | 7 ++++ 7 files changed, 71 insertions(+), 8 deletions(-) create mode 100644 libs/fabric/src/lib/utils/get-data-attributes.ts create mode 100644 libs/fabric/src/lib/utils/kebab-case.ts diff --git a/apps/demo/src/app/app.component.html b/apps/demo/src/app/app.component.html index b55900f6..ff0bdb5e 100644 --- a/apps/demo/src/app/app.component.html +++ b/apps/demo/src/app/app.component.html @@ -41,6 +41,11 @@

Getting up and running...

Tab 3's content
+ + + + + Getting up and running... text="Run" [iconProps]="{ iconName: 'CaretRight' }" [disabled]="runDisabled" + data-track-type="run" > Getting up and running... [iconProps]="{ iconName: 'Add' }" (click)="onNewClicked()" > - + + + + implements OnInit, AfterContentInit, OnDestroy { @@ -167,6 +168,7 @@ export abstract class FabBaseButtonComponent extends ReactWrapperComponent { directive.click.emit({ ev: ev && ev.nativeEvent, item: item }); }, diff --git a/libs/fabric/src/lib/components/command-bar/directives/command-bar-item.directives.ts b/libs/fabric/src/lib/components/command-bar/directives/command-bar-item.directives.ts index 6002ca8c..b80c6dd3 100644 --- a/libs/fabric/src/lib/components/command-bar/directives/command-bar-item.directives.ts +++ b/libs/fabric/src/lib/components/command-bar/directives/command-bar-item.directives.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { ContentChild, Directive, Input, TemplateRef } from '@angular/core'; +import { ContentChild, Directive, Input, TemplateRef, ElementRef } from '@angular/core'; import { ContextualMenuItemDirective } from '../../contextual-menu/directives/contextual-menu-item.directive'; import { ItemChangedPayload } from '../../core/declarative/item-changed.payload'; import { @@ -37,4 +37,8 @@ export class CommandBarItemDirective extends ContextualMenuItemDirective impleme @Input() cacheKey?: ICommandBarItemOptions['cacheKey']; @Input() renderedInOverflow?: ICommandBarItemOptions['renderedInOverflow']; @Input() commandBarButtonAs?: ICommandBarItemOptions['commandBarButtonAs']; + + constructor(elementRef: ElementRef) { + super(elementRef); + } } diff --git a/libs/fabric/src/lib/components/command-bar/directives/command-bar-items.directives.ts b/libs/fabric/src/lib/components/command-bar/directives/command-bar-items.directives.ts index 2b6ef772..4fe401e9 100644 --- a/libs/fabric/src/lib/components/command-bar/directives/command-bar-items.directives.ts +++ b/libs/fabric/src/lib/components/command-bar/directives/command-bar-items.directives.ts @@ -6,6 +6,7 @@ import { ContentChildren, Directive, QueryList } from '@angular/core'; import { ChangeableItemsDirective } from '../../core/shared/changeable-items.directive'; import { ICommandBarItemOptions } from '../command-bar.component'; import { CommandBarItemDirective } from './command-bar-item.directives'; +import { getDataAttributes } from '../../../utils/get-data-attributes'; export abstract class CommandBarItemsDirectiveBase extends ChangeableItemsDirective { abstract readonly directiveItems: QueryList; @@ -13,8 +14,9 @@ export abstract class CommandBarItemsDirectiveBase extends ChangeableItemsDirect get items() { return ( this.directiveItems && - this.directiveItems.map(directiveItem => ({ + this.directiveItems.map((directiveItem: CommandBarItemDirective) => ({ ...directiveItem, + ...getDataAttributes(directiveItem.elementRef.nativeElement, true), onClick: (ev, item) => { directiveItem.click.emit({ ev: ev && ev.nativeEvent, diff --git a/libs/fabric/src/lib/components/contextual-menu/directives/contextual-menu-item.directive.ts b/libs/fabric/src/lib/components/contextual-menu/directives/contextual-menu-item.directive.ts index da641e99..a40dc829 100644 --- a/libs/fabric/src/lib/components/contextual-menu/directives/contextual-menu-item.directive.ts +++ b/libs/fabric/src/lib/components/contextual-menu/directives/contextual-menu-item.directive.ts @@ -12,6 +12,7 @@ import { QueryList, ContentChild, TemplateRef, + ElementRef, } from '@angular/core'; import { IContextualMenuItem } from 'office-ui-fabric-react'; import { KnownKeys, InputRendererOptions } from '@angular-react/core'; @@ -20,6 +21,7 @@ import { OnChanges } from '../../../declarations/angular/typed-changes'; import { ItemChangedPayload } from '../../core/declarative/item-changed.payload'; import { ChangeableItemsHelper, IChangeableItemsContainer } from '../../core/shared/changeable-helper'; import { ChangeableItemDirective } from '../../core/shared/changeable-item.directive'; +import { getDataAttributes } from '../../../utils/get-data-attributes'; export type ContextualMenuItemChangedPayload = ItemChangedPayload< IContextualMenuItemOptions['key'], @@ -101,6 +103,10 @@ export class ContextualMenuItemDirective extends ChangeableItemDirective) { + super(); + } + private _changeableItemsHelper: ChangeableItemsHelper; ngAfterContentInit() { @@ -129,6 +135,7 @@ export class ContextualMenuItemDirective extends ChangeableItemDirective { directive.click.emit({ ev: ev && ev.nativeEvent, item: item }); }, @@ -143,6 +150,11 @@ export interface IContextualMenuItemOptions readonly renderIcon?: InputRendererOptions; readonly render?: InputRendererOptions; readonly data?: TData; + + /** + * For any attributes like data-* etc. + */ + [propertyName: string]: any; } export interface IContextualMenuItemOptionsRenderContext { diff --git a/libs/fabric/src/lib/utils/get-data-attributes.ts b/libs/fabric/src/lib/utils/get-data-attributes.ts new file mode 100644 index 00000000..48678a39 --- /dev/null +++ b/libs/fabric/src/lib/utils/get-data-attributes.ts @@ -0,0 +1,32 @@ +import { kebabCase } from './kebab-case'; + +/** + * Gets the data attributes on an `HTMLElement`. + * + * @example +```keepDataPrefix === false```: +```html +
-> { 'service': 'Foo', 'service-type': 'Bar' } +``` + +```keepDataPrefix === true```: +```html +
-> { 'data-service': 'Foo', 'data-service-type': 'Bar' } +``` + */ +export function getDataAttributes( + element: T, + keepDataPrefix: boolean = false +): Record { + return Object.entries(element.dataset).reduce( + (acc, [key, value]) => ({ + ...acc, + [calculateKey(key, keepDataPrefix)]: value, + }), + {} + ); +} + +function calculateKey(key: string, keepDataPrefix: boolean): string { + return `${keepDataPrefix && 'data-'}${kebabCase(key)}`; +} diff --git a/libs/fabric/src/lib/utils/kebab-case.ts b/libs/fabric/src/lib/utils/kebab-case.ts new file mode 100644 index 00000000..55294c74 --- /dev/null +++ b/libs/fabric/src/lib/utils/kebab-case.ts @@ -0,0 +1,7 @@ +/** Implementation borrowed from https://github.com/joakimbeng/kebab-case */ + +const KEBAB_REGEX = /[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g; + +export function kebabCase(str: string) { + return str.replace(KEBAB_REGEX, match => '-' + match.toLowerCase()); +}