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

Initial set of Keytips work in experiments #4062

Merged
merged 84 commits into from
Mar 2, 2018

Conversation

kelseyyoung
Copy link
Contributor

@kelseyyoung kelseyyoung commented Feb 21, 2018

Pull request checklist

  • Addresses an existing issue: Fixes #0000
  • Include a change request file using $ npm run change

Description of changes

Experiments: Initial set of work for Keytips. See Keytip and KeytipLayer demo pages in experiments

Utilities: Add missing key codes to KeyCodes enum

keytiplayerexample

Focus areas to test

kelseyyoung and others added 30 commits February 1, 2018 16:50
- Made key sequences in KeytipLayer optional again
- Add utility constants, refactored code
- Moved IKeySequence to string code from KeytipManager to IKeytipSequence, also moved tests
- Manually add Keytip through manager after mount for experiments
- Keytips will now hide on scroll
- Introduce idea of modifier key codes
- Split processInput with processTransitionInput
- Remove ID and KeytipTarget from Keytip props
// Try to find keytipProps in previousKeytips to update
let keytipToUpdateIndex = -1;
for (let i = 0; i < previousKeytips.length; i++) {
if (fullKeySequencesAreEqual(keytipProps.keySequences, previousKeytips[i].keySequences)) {
Copy link
Member

Choose a reason for hiding this comment

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

Use findIndex from the utilities package here instead of rolling your own loop.

* @returns {boolean} T/F if 'sequences' contains 'seq'
*/
export function keySequencesContain(sequences: IKeySequence[], seq: IKeySequence): boolean {
for (let i = 0; i < sequences.length; i++) {
Copy link
Member

Choose a reason for hiding this comment

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

Seems like this is just an implemetation of includes, unfortunately this doesn't work in IE11, but we can add an implementation in utilities (like there is for findIndex) to use to reduce boilerplate here in a few places. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@christiango can we use includes() if we have an array of objects? (in this case, IKeySequence[]) I would think then we would need an object deep equals function...

at its base IKeySequence.keys is an array of strings, but that is an equality check we do with .join(), it wouldn't be an includes() check there

Copy link
Member

Choose a reason for hiding this comment

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

You can use find if you need more elaborate comparison logic

*/
export function convertSequencesToKeytipID(keySequences: IKeySequence[]): string {
let conversion = ktpPrefix;
for (let keySequence of keySequences) {
Copy link
Member

Choose a reason for hiding this comment

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

Seems like a good candidate for array.prototype.reduce instead of a for of loop

if (mod2Copy.length !== 0) {
return false;
}
}
Copy link
Member

Choose a reason for hiding this comment

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

This is N^2, can we use a JavaScript set instead? I'm not sure what the policy on using Set is in fabric, it is supported by IE11, but it is in typescripts ES2015.collections library, which is not included by default when transpiling to ES5. Thoughts @dzearing ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What do you think about sorting the two arrays and looping through? The arrays are just of enums (so numbers) so they can be sorted naturally

Copy link
Member

Choose a reason for hiding this comment

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

How big do you expect these arrays to get? Sorting will still be slower than using the set, which might matter if these lists get pretty long or this code runs a lot.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The arrays are just a list of modifiers (Alt, Shift, Ctrl). We only have 5 defined now so that would be the max, but the developer wouldn't define more then 2-3 (otherwise the user has to press 5 keys to enable keytip mode)

Copy link
Member

Choose a reason for hiding this comment

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

Sorting is fine then

* @returns {boolean} T/F if 'keys' contains 'key'
*/
export function transitionKeysContain(keys: IKeytipTransitionKey[], key: IKeytipTransitionKey): boolean {
for (let i = 0; i < keys.length; i++) {
Copy link
Member

Choose a reason for hiding this comment

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

In general, go through all the for loops we have here and see if we can use some of the built in higher order functions in JavaScript (or the equivalent implementation in fabric utilities when that API is not supported in IE11)

Copy link
Contributor Author

@kelseyyoung kelseyyoung Feb 22, 2018

Choose a reason for hiding this comment

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

Yeah there were a few places where we wanted to use those functions but didn't because of compatibility. I didn't know there would be equivalent ones in fabric so thanks for pointing that out!

<div style={ divStyle }>
<div>
<ActionButton
data-ktp-id={ convertSequencesToKeytipID(this.keytipMap.Pivot1Keytip.keySequences) }
Copy link
Member

@dzearing dzearing Mar 1, 2018

Choose a reason for hiding this comment

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

feedback (not required to change in this PR)

abstract in a helper function so that caller doesn't know about data-*.

Also this is quite verbose. There needs to be a better experience than to call an API convertSequencesToKeytipID here for the caller.

If we go with the helper, maybe BaseComponent could auto dispose the cached options from global state, if that's what we do to get access to the global state.

private _keytips = this.autoDispose(registerKeytips({
  pivot1: { options },
  pivot2: { options }
}));

// ...and then:

<ActionButton
   { ...this._keytips.pivot1 }
/>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right, this is only because the keytipProps haven't been added to ActionButton. We have to mock it right now because we haven't moved to office-ui-fabric-react. Hardcoding the data-ktp-id would be replaced with just defining keytipProps

// Manually add keytips to the KeytipManager for now
// This should really be done in each component
for (let component of Object.keys(this.keytipMap)) {
registerKeytip(this.keytipMap[component]);
Copy link
Member

Choose a reason for hiding this comment

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

I see a registerKeytip call but no unregisterKeytip.

I am really nervous about this pattern because if it's easy for you to forget to do this, it will be easy for partners to as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, this is also mocked, the register and unregister should only be in the component render() and componentWillUnmount() (I know we have discussed this, we can try to come up with a better solution). In this sample we don't have any dynamic DOM elements so we aren't doing any unregistering. When we move to the normal package we will have keytips on menus, etc

Copy link
Member

Choose a reason for hiding this comment

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

isn't the sample itself dynamic? E.g. you will have a memory leak if you go to the page, then to another, then back to the original.

*
* @param keytipProps - IKeytipProps to add to this layer
*/
public registerKeytip(keytipProps: IKeytipProps): void {
Copy link
Member

Choose a reason for hiding this comment

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

Rather than an API which takes a single keytip, why not take in a map of keytips?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

register should only be called from one component''s render() at a time, so I don't see a use case for registering multiple right now but if we come across one we can definitely do it! would be very easy

Copy link
Member

@dzearing dzearing left a comment

Choose a reason for hiding this comment

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

Added some feedback! But let's get it in and iterate.

@dzearing dzearing merged commit 44ca7b8 into microsoft:master Mar 2, 2018
Markionium added a commit to Markionium/office-ui-fabric-react that referenced this pull request Mar 4, 2018
* master: (30 commits)
  Addressing Issue microsoft#4147 - Nav Buttons have invalid `aria-describedby` value (microsoft#4159)
  Fix issue 3608: DetailsList horizontal scroll (microsoft#4164)
  Update package.json
  Image SCSS to MergeStyles Part 2: Style Conversion (microsoft#4151)
  Applying package updates.
  [SpinButton] Consistent styles to Button and TextField (microsoft#4098)
  ChoiceGroup: Flex layout for image and icon options (microsoft#4137)
  Initial set of Keytips work in experiments (microsoft#4062)
  Updating tsconfig in button bundle.
  BaseExtendedPicker: Create contextmenu for renderedItem, fix auto focus (microsoft#3954)
  Dropdown: Custom render options for multiselect - Bug fix microsoft#3571  (microsoft#3589)
  Fix documentcard theming (microsoft#4155)
  BasePicker: Fix not used onBlur callback of inputProps (microsoft#4000) (microsoft#4131)
  Allow Elements as Callout targets (microsoft#4134)
  CommandBar: Fixed null commandItemWidths (microsoft#4136)
  add check for when this.suggestionElement may be undefined (microsoft#4157)
  Addressing Issue microsoft#4143 - is-selected missing on ms-Nav-link (microsoft#4158)
  Aria selected (microsoft#4161)
  Move fabric to TypeScript 2.7.2 (microsoft#4153)
  Updating SearchBox examples to have the removed string in placeholder prop.
  ...
Markionium added a commit to Markionium/office-ui-fabric-react that referenced this pull request Mar 5, 2018
* master: (51 commits)
  Applying package updates.
  No unused variable (microsoft#4173)
  Use correct _list ref string (microsoft#4168)
  Nav: wire a link to expand/collapse behavior if it has no URL but has children (microsoft#4171)
  Addressing Issue microsoft#4147 - Nav Buttons have invalid `aria-describedby` value (microsoft#4159)
  Fix issue 3608: DetailsList horizontal scroll (microsoft#4164)
  Update package.json
  Image SCSS to MergeStyles Part 2: Style Conversion (microsoft#4151)
  Applying package updates.
  [SpinButton] Consistent styles to Button and TextField (microsoft#4098)
  ChoiceGroup: Flex layout for image and icon options (microsoft#4137)
  Initial set of Keytips work in experiments (microsoft#4062)
  Updating tsconfig in button bundle.
  BaseExtendedPicker: Create contextmenu for renderedItem, fix auto focus (microsoft#3954)
  Dropdown: Custom render options for multiselect - Bug fix microsoft#3571  (microsoft#3589)
  Fix documentcard theming (microsoft#4155)
  BasePicker: Fix not used onBlur callback of inputProps (microsoft#4000) (microsoft#4131)
  Allow Elements as Callout targets (microsoft#4134)
  CommandBar: Fixed null commandItemWidths (microsoft#4136)
  add check for when this.suggestionElement may be undefined (microsoft#4157)
  ...
chrismohr pushed a commit to chrismohr/office-ui-fabric-react that referenced this pull request Apr 17, 2018
* Adding files to experiments

* Fix most tslint errors

* Fix initial tslint errors

* Fix other tslint errors

* Fix last compiling issues

* Add keysequence files

* Add pages to example app

* Removed IKeySequence files in packages/utilities
- Made key sequences in KeytipLayer optional again
- Add utility constants, refactored code
- Moved IKeySequence to string code from KeytipManager to IKeytipSequence, also moved tests

* First pass of writing processInput

* Update KeytipLayer Example to use data attribute
- Manually add Keytip through manager after mount for experiments

* Add Keytipsequencestarts with tests

* Add matched and partially matched nodes tests

* Add keysequenceStarts with tests and partial and exact node matched tests

* Add more tests to keysquences

* Add initial processInput tests

* Add initial processInput tests

* Clean up unused imports

* Add partial nodes tests

* Show/hide methods for the tree nodes

* Add hasChildrenNodes prop and a test for it

* Moved function to comply with tslint

* Get keytip to show on starting sequence in example
- Keytips will now hide on scroll

* Refactor KeySequences to use strings instead of KeyCodes
- Introduce idea of modifier key codes
- Split processInput with processTransitionInput

* disable and enable keytipmode correctly

* Add more items to KeytipLayer example
- Remove ID and KeytipTarget from Keytip props

* Fix unit tests

* Fix exit points for keys

* Exit when scrolled and enter keytipmode again

* Add exit sequences

* Fix broken unit tests

* Add modifier to example

* Fix hierarchy bugs

* Add more examples  keytips demo

* Add get execture func

* Make processInput consume only one key

* Change goback to return

* Add overflowsetSequence props

* Documentation and refactoring

* Unregister keytip functionality
- Register and unregister utility functions
- removeNode tests for KeytipTree

* Remove IKeytipTranstionSequence, transition keys can only be one key now

* Add comments algorithm

* Change addNode to receive keytipProps

* Remove duplicate identifiers

* Allow for multiple modifier keys
- Extract transition key out into own files and tests

* Fix import errors by moving KeytipManager and Tree to utilities/keytip
- Remove visible from the state of the Keytip

* Show keytip right away if its parent is currentKeytip

* Add tests for overflow node adding

* Delete keytiptree tests from wrong directory

* Re-add tests to overflowset sequence for add node

* Fix go back and clean current Sequence array

* registerKeytip update functionality
- Removed visible property from KeytipTreeNode

* Updated KeytipLayer demo

* Add test for keytiplink process input

* Added logic for keytiplink in remove and added unit test

* Add tests for matching disabled nodes

* Make content required on IKeytipProps
- Add element to onExecute and onReturn functions
- Update KeytipLayer example to click buttons
- Removed index.ts files for utils to avoid import issues

* Remove outdated comments

* Remove outdated comment

* Comments and small refactoring

* Add refactoring changes for ketip and keytipcontent

* Add tests

* Very basic layer test

* Added examples for Keytip page
- Added some documentation and Dos/Donts

* Remove unused keytipstate

* Remove unused files

* Small fixes

* Fix Keytip language example

* Make KeyCodes a const enum again
- Move and rename ModifierKeyCodes to Keytip.types to prevent errors in testing

* Add rush change files

* Add offset default prop and test UI for offset

* Improve code with Fabric array utils
- Use reduce or map where applicable
@microsoft microsoft locked as resolved and limited conversation to collaborators Aug 31, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants