Using the SearchBox in Office UI Fabric React CommandBar - office-ui-fabric

I'm trying to work out how to use the built in search box component on the CommandBar within Office UI Fabric React
The documentation at http://dev.office.com/fabric#/components/commandbar doesn't seem to cover it.
Specifically I'd like to know how to get the search term entered and to execute a search

I've checked the source: https://github.com/OfficeDev/office-ui-fabric-react/blob/master/packages/office-ui-fabric-react/src/components/CommandBar/CommandBar.tsx
The searchbox is implemented as a simple input element:
if (isSearchBoxVisible) {
searchBox = (
<div className={ css('ms-CommandBarSearch', styles.search) } ref='searchSurface'>
<input className={ css('ms-CommandBarSearch-input', styles.searchInput) } type='text' placeholder={ searchPlaceholderText } />
<div className={ css(
'ms-CommandBarSearch-iconWrapper ms-CommandBarSearch-iconSearchWrapper',
styles.searchIconWrapper, styles.searchIconSearchWrapper) }>
<i className={ css('ms-Icon ms-Icon--Search') }></i>
</div>
<div className={ css(
'ms-CommandBarSearch-iconWrapper ms-CommandBarSearch-iconClearWrapper ms-font-s',
styles.searchIconWrapper,
styles.searchIconClearWrapper
) }>
<i className={ css('ms-Icon ms-Icon--Cancel') }></i>
</div>
</div>
);
}
It can be accessed with the refs property:
public refs: {
[key: string]: React.ReactInstance;
commandSurface: HTMLElement;
farCommandSurface: HTMLElement;
commandBarRegion: HTMLElement;
searchSurface: HTMLElement;
focusZone: FocusZone;
};
Now you could try to use the standard properties and events of an input element. (I haven't tried.)

Related

Vue.js 3 use autofocus on input with ref inside a method

I worked with Vue2, but I recently try Vue 3.
I have simple problem:
<input ref="myinput" />
<button #click="submitData" />
I want to set "focus" on "myinput", inside function "submitData".
In Vue 2 it is simple (this.$refs ...), but in Vue 3, they made it complicated.
I saw example with "setup", but is no use for me + I think you can only access "value" from element.
Is there any way to execute "focus" on on element inside methods?
You are still able to do the same thing using Vue 3, but if you work with composition api there's some difference :
Options API :
const {
createApp
} = Vue;
const App = {
data() {
return {
}
},
methods: {
submitData() {
this.$refs.myinput.focus()
}
},
mounted() {
}
}
const app = createApp(App)
app.mount('#app')
<script src="https://unpkg.com/vue#3.0.0-rc.11/dist/vue.global.prod.js"></script>
<div id="app">
Vue 3 app
<input ref="myinput" />
<button #click="submitData">
Submit
</button>
</div>
composition API:
const {
createApp,
ref,
onMounted,
} = Vue;
const App = {
setup() {
const myinput = ref(null)
function submitData() {
myinput.value.focus()
}
return {
myinput,
submitData
}
}
}
const app = createApp(App)
app.mount('#app')
<script src="https://unpkg.com/vue#3.0.0-rc.11/dist/vue.global.prod.js"></script>
<div id="app">
Vue 3 app
<input ref="myinput" />
<button #click="submitData">
Submit
</button>
</div>
In case someone comes to this question looking for a way to set the autofocus of a specific element in Vue3, you can achieve it using a Vue Custom Directive
const { createApp, onMounted } = Vue;
const app = createApp({})
// Register a global custom directive called `v-focus`
app.directive('focus', {
// When the bound element is mounted into the DOM...
mounted(el) {
// Focus the element
el.focus()
}
})
app.mount('#app')
<script src="https://unpkg.com/vue#next"></script>
<div id="app">
<input />
<input v-focus />
<input />
</div>
In some cases when the input is hidden under a v-show or v-if it is necessary to do a nextTick for the focus to work.
<span
v-show="!editMode"
#click="handleEditionMode"
>
{{ content }}
</span>
<input
v-show="editMode"
ref="input"
v-model="content"
aria-describedby="item-content"
name="content"
type="text"
tabindex="0"
#focusout="editMode = false"
#keydown.enter="editMode = false"
/>
const input = ref(null),
editMode = ref(false);
const handleEditionMode = () => {
editMode.value = true;
nextTick(() => {
input.value.focus();
});
};
Easiest answer I found is missing here
<input type="text" autofocus />
I was trying to select a specific input upon loading the form component.
The above examples were not useful, so I figured it out myself.
This is far simpler, IMHO. Add 1 ref tag and 1 line of code in the mounted hook.
Place a ref tag on the item you'd like to focus. Here I named it "formStart" but you can name yours whatever you like.
<form #submit.prevent="createNewRoute">
<label for="code">Code</label>
<input v-model="code" id="code" type="text" ref="formStart" /> <!-- REF TAG HERE -->
<label for="type">Type</label>
<input v-model="type" id="type" type="text" />
/* Other inputs hidden for simplicity */
<button type="submit">Add Route</button>
</form>
Reference that ref tag in the mounted hook and focus() it.
<script>
export default {
/* Other options hidden for simplicity */
mounted() {
this.$refs.formStart.focus(); // FOCUS ELEMENT HERE
},
};
</script>
Another vanilla solution is:
document.getElementById("code")?.focus()
to be called on onMounted
<div
v-if="openmodal"
tabindex="0"
v-focus
#keydown.right="() => nextimage(1)"
>
</div>
i used methods of #webcu and added tabindex="0" it's work!

v-for delay dom patching when data update

I'm noticing that the v-for I'm using to render some images inside a component, when data are updated using the event bus,I will have a little delay in DOM content replacing. Is there a way to solve this little problem?
NB: I can't replicate the data passed because it's a 50 elements list of images urls and are provided by an external api service.
<template>
<div class="row m-0 pl-5 pr-5">
<div class="col-4 col-img hide p-0" v-for="(img, idx) in feed" :key="idx" v-observe-visibility="visibilityChanged">
<img class="img-fluid w-100 h-100 ig-image" :src="img.url">
<div class="card-img-overlay text-center">
<p class="text-white">{{ img.likes }} <i class="fas fa-heart"></i> {{ img.comments }} <i class="fas fa-comment"></i></p>
</div>
<a class="stretched-link" href="#image-modal" data-toggle="modal" v-on:click.prevent="zoomImage(img.url)"></a>
</div>
</div>
</template>
<script>
import { EventBus } from '#/standalone/event-bus'
export default {
name: 'UserMedia',
data() {
return {
feed: null,
imgUrl: null
}
},
mounted() {
EventBus.$on('profile-media', (media) => {
this.$set(this, 'feed', media)
})
},
methods: {
zoomImage(url) {
this.$set(this, 'imgUrl', url)
},
visibilityChanged(isVisible, el) {
if(isVisible){
el.target.classList.remove('hide')
el.target.classList.add('show')
}
}
}
}
</script>
<style scoped>
.col-img {
height: 420px;
}
</style>
Since if you change the data 'feed', it will take time to load the images but inorder to load small size images prior to heavy size for good user experience you can use some very good npm packages:
npm i vue-lazyload (I have used it and recommend this one)
npm i v-lazy-image (I haven't used yet but you can explore this as well)

Cannot trigger 'onChanged' delegate with Office UI Fabric Dropdown inside a Jest/Enzyme tests

I need to unit test a callback provided to 'onChanged' event in an office-ui-fabric Dropdown (with spyOn and expect(callback).toHaveBeenCalled()). The problem is that I don't know how to trigger this event? I tried to look at the DOM but there are no html select tag that I can trigger. I also tried to change the state of the selected item (on the dropdown component) but I get an error with Jest telling me that I can only change the state of a root element (I tried with shallow, mount and dive into the dropdown component). Is there an easy way to accomplish this?
I am using Jest with Enzyme and this code:
const div = document.createElement('div');
ReactDOM.render(<Dropdown
label='My label'
placeHolder='My placeholder'
options={[
{ key: 'A', text: 'Option a' },
{ key: 'B', text: 'Option b' },
]}
onChanged={() => { return; }} />, div);
const dropdownContainer = div.querySelector('.ms-Dropdown') as HTMLElement;
ReactTestUtils.Simulate.click(dropdownContainer);
And it outputs this html:
<div class="ms-Dropdown-container">
<label class="ms-Label ms-Dropdown-label root-37" id="Dropdown0-label" for="Dropdown0">My label</label>
<div data-is-focusable="true" id="Dropdown0" tabindex="0" aria-expanded="true" role="listbox" aria-autocomplete="none" aria-live="off" aria-describedby="Dropdown0-option" class="ms-Dropdown root_f16b4a0d is-open" aria-owns="Dropdown0-list">
<span id="Dropdown0-option" class="ms-Dropdown-title title_f16b4a0d ms-Dropdown-titleIsPlaceHolder titleIsPlaceHolder_f16b4a0d" aria-atomic="true" role="listbox" aria-readonly="true">
<span>My placeholder</span>
</span>
<span class="ms-Dropdown-caretDownWrapper caretDownWrapper_f16b4a0d">
<i data-icon-name="ChevronDown" class="ms-Dropdown-caretDown caretDown_f16b4a0d root-39" role="presentation" aria-hidden="true"></i>
</span>
</div>
<span class="ms-Layer"/>
</div>
There is no ".ms-Dropdown-item"
You should be able to use querySelector with ID ms-Dropdown and then ReactTestUtils.Simulate.click. There are examples of this in the existing Dropdown unit tests:
it('issues the onChanged callback when the selected item is different', () => {
const container = document.createElement('div');
let dropdownRoot: HTMLElement | undefined;
document.body.appendChild(container);
const onChangedSpy = jasmine.createSpy('onChanged');
try {
ReactDOM.render(
<Dropdown label="testgroup" defaultSelectedKey="1" onChanged={onChangedSpy} options={DEFAULT_OPTIONS} />,
container
);
dropdownRoot = container.querySelector('.ms-Dropdown') as HTMLElement;
ReactTestUtils.Simulate.click(dropdownRoot);
const secondItemElement = document.querySelector('.ms-Dropdown-item[data-index="2"]') as HTMLElement;
ReactTestUtils.Simulate.click(secondItemElement);
} finally {
expect(onChangedSpy).toHaveBeenCalledWith(DEFAULT_OPTIONS[2], 2);
}
});
Unit test source file:
https://github.com/OfficeDev/office-ui-fabric-react/blob/master/packages/office-ui-fabric-react/src/components/Dropdown/Dropdown.test.tsx

Custom Rules in Kendo Ui For Vue Validator

I start using Kendo UI for Vue.
I want to use Kendo Validator Wrapper and it seems to be very simple for basic validation.
The sample provided:
<div id="vueapp" class="vue-app">
<form id="ticketsForm" ref="myForm" v-kendo-validator #submit.prevent="onSubmit">
<input type="search"
id="search"
name="search"
required
validationMessage="Field is required"
class="k-textbox" />
<span class="k-invalid-msg" data-for="search"></span>
<div>
<button class="k-button k-primary" type="submit">Submit</button>
</div>
</form>
<div class="status"></div>
For custom validation Kendo provide rules in configuration section:
https://docs.telerik.com/kendo-ui/api/javascript/ui/validator/configuration/rules
... you can help me, please, to understand how I can setup custom rules with this wrapper?
Thank-you
You can define the validator this way - v-kendo-validator="getRules()" and return the object of the rules in the method getRules. Here is a plunked.
getRules: function () {
return {
rules: {
customRule1: function (input) {
// all of the input must have a value
return kendo.jQuery.trim(input.val()) !== ''
},
customRule2: function (input) {
// only 'Tom' will be valid value for the username input
return input.val() === 'Tom'
}
},
messages: {
customRule1: 'All fields are required',
customRule2: 'Your UserName must be Tom'
}
}
}

When using conditional rendering, how do I prevent repeating the child components on each condition?

Scenario
I have a custom button component in Vue:
<custom-button type="link">Save</custom-button>
This is its template:
// custom-button.vue
<template>
<a v-if="type === 'link'" :href="href">
<span class="btn-label"><slot></slot></span>
</a>
<button v-else :type="type">
<span class="btn-label"><slot></slot></span>
</button>
</template>
You can see from the template that it has a type prop. If the type is link, instead of the <button> element, I am using <a>.
Question
You'll notice from the template that I repeated the child component, i.e. <span class="btn-label"><slot></slot></span> on both root components. How do I make it so that I won't have to repeat the child components?
In JSX, it's pretty straightforward. I just have to assign the child component to a variable:
const label = <span class="btn-label">{text}</span>
return (type === 'link')
? <a href={href}>{label}</a>
: <button type={type}>{label}</button>
In this situation, I would probably opt to write the render function directly since the template is small (with or without JSX), but if you want to use a template then you can use the <component> component to dynamically choose what you want to render as that element, like this:
Vue.component('custom-button', {
template: '#custom-button',
props: [
'type',
'href',
],
computed: {
props() {
return this.type === 'link'
? { is: 'a', href: this.href }
: { is: 'button', type: this.type };
},
},
});
new Vue({
el: '#app',
});
<script src="https://rawgit.com/vuejs/vue/dev/dist/vue.js"></script>
<div id="app">
<custom-button type="button">Button</custom-button>
<custom-button type="submit">Submit</custom-button>
<custom-button type="link" href="http://www.google.com">Link</custom-button>
</div>
<template id="custom-button">
<component v-bind="props">
<span class="btn-label"><slot></slot></span>
</component>
</template>
Well you could always create a locally registered component...
// in custom-button.vue
components : {
'label' : {template : '<span class="btn-label"><slot></slot></span>'}
}