diff --git a/404.html b/404.html old mode 100755 new mode 100644 diff --git a/assets/api-access-approved.jpg b/assets/api-access-approved.jpg old mode 100755 new mode 100644 diff --git a/assets/api-access-pending.jpg b/assets/api-access-pending.jpg old mode 100755 new mode 100644 diff --git a/assets/app_catalog.png b/assets/app_catalog.png old mode 100755 new mode 100644 diff --git a/assets/cards_layout_options.png b/assets/cards_layout_options.png old mode 100755 new mode 100644 diff --git a/assets/extensibility/adaptive_cards_templating.png b/assets/extensibility/adaptive_cards_templating.png old mode 100755 new mode 100644 diff --git a/assets/extensibility/extensibility_configuration.png b/assets/extensibility/extensibility_configuration.png old mode 100755 new mode 100644 diff --git a/assets/extensibility/extensibility_configuration_manifest.png b/assets/extensibility/extensibility_configuration_manifest.png old mode 100755 new mode 100644 diff --git a/assets/extensibility/handlebars_templating.png b/assets/extensibility/handlebars_templating.png old mode 100755 new mode 100644 diff --git a/assets/extensibility/implement_interface.png b/assets/extensibility/implement_interface.png old mode 100755 new mode 100644 diff --git a/assets/extensibility/layout/custom_layout.png b/assets/extensibility/layout/custom_layout.png old mode 100755 new mode 100644 diff --git a/assets/extensibility/layout/layout_properties.png b/assets/extensibility/layout/layout_properties.png old mode 100755 new mode 100644 diff --git a/assets/extensibility/library_manifest_id.png b/assets/extensibility/library_manifest_id.png old mode 100755 new mode 100644 diff --git a/assets/extensibility/web_components/collapsible_component.png b/assets/extensibility/web_components/collapsible_component.png old mode 100755 new mode 100644 diff --git a/assets/extensibility/web_components/custom_web_component.png b/assets/extensibility/web_components/custom_web_component.png old mode 100755 new mode 100644 diff --git a/assets/extensibility/web_components/debug_types.png b/assets/extensibility/web_components/debug_types.png old mode 100755 new mode 100644 diff --git a/assets/extensibility/web_components/document_card_component.png b/assets/extensibility/web_components/document_card_component.png old mode 100755 new mode 100644 diff --git a/assets/extensibility/web_components/fileicon_component.png b/assets/extensibility/web_components/fileicon_component.png old mode 100755 new mode 100644 diff --git a/assets/extensibility/web_components/filepreview_component.png b/assets/extensibility/web_components/filepreview_component.png old mode 100755 new mode 100644 diff --git a/assets/extensibility/web_components/icon_component.png b/assets/extensibility/web_components/icon_component.png old mode 100755 new mode 100644 diff --git a/assets/extensibility/web_components/panel_component.png b/assets/extensibility/web_components/panel_component.png old mode 100755 new mode 100644 diff --git a/assets/extensibility/web_components/persona_component.png b/assets/extensibility/web_components/persona_component.png old mode 100755 new mode 100644 diff --git a/assets/extra.css b/assets/extra.css old mode 100755 new mode 100644 index 25de67d51..b38f5be35 --- a/assets/extra.css +++ b/assets/extra.css @@ -1,22 +1,22 @@ -.center { - display: block; - margin-left: auto; - margin-right: auto; -} - -.logo { - width: 150px; -} - -.md-header { - background-color: #0078d4; -} - -.md-tabs { - background-color: #ecf0f1; -} - -.md-tabs__link { - opacity: 1; - color: #0078d4; +.center { + display: block; + margin-left: auto; + margin-right: auto; +} + +.logo { + width: 150px; +} + +.md-header { + background-color: #0078d4; +} + +.md-tabs { + background-color: #ecf0f1; +} + +.md-tabs__link { + opacity: 1; + color: #0078d4; } \ No newline at end of file diff --git a/assets/images/favicon.png b/assets/images/favicon.png old mode 100755 new mode 100644 diff --git a/assets/javascripts/bundle.febc23d1.min.js b/assets/javascripts/bundle.febc23d1.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/bundle.febc23d1.min.js.map b/assets/javascripts/bundle.febc23d1.min.js.map old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/min/lunr.ar.min.js b/assets/javascripts/lunr/min/lunr.ar.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/min/lunr.da.min.js b/assets/javascripts/lunr/min/lunr.da.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/min/lunr.de.min.js b/assets/javascripts/lunr/min/lunr.de.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/min/lunr.du.min.js b/assets/javascripts/lunr/min/lunr.du.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/min/lunr.es.min.js b/assets/javascripts/lunr/min/lunr.es.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/min/lunr.fi.min.js b/assets/javascripts/lunr/min/lunr.fi.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/min/lunr.fr.min.js b/assets/javascripts/lunr/min/lunr.fr.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/min/lunr.hi.min.js b/assets/javascripts/lunr/min/lunr.hi.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/min/lunr.hu.min.js b/assets/javascripts/lunr/min/lunr.hu.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/min/lunr.it.min.js b/assets/javascripts/lunr/min/lunr.it.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/min/lunr.ja.min.js b/assets/javascripts/lunr/min/lunr.ja.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/min/lunr.jp.min.js b/assets/javascripts/lunr/min/lunr.jp.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/min/lunr.multi.min.js b/assets/javascripts/lunr/min/lunr.multi.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/min/lunr.nl.min.js b/assets/javascripts/lunr/min/lunr.nl.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/min/lunr.no.min.js b/assets/javascripts/lunr/min/lunr.no.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/min/lunr.pt.min.js b/assets/javascripts/lunr/min/lunr.pt.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/min/lunr.ro.min.js b/assets/javascripts/lunr/min/lunr.ro.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/min/lunr.ru.min.js b/assets/javascripts/lunr/min/lunr.ru.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/min/lunr.stemmer.support.min.js b/assets/javascripts/lunr/min/lunr.stemmer.support.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/min/lunr.sv.min.js b/assets/javascripts/lunr/min/lunr.sv.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/min/lunr.th.min.js b/assets/javascripts/lunr/min/lunr.th.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/min/lunr.tr.min.js b/assets/javascripts/lunr/min/lunr.tr.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/min/lunr.vi.min.js b/assets/javascripts/lunr/min/lunr.vi.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/min/lunr.zh.min.js b/assets/javascripts/lunr/min/lunr.zh.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/tinyseg.js b/assets/javascripts/lunr/tinyseg.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/lunr/wordcut.js b/assets/javascripts/lunr/wordcut.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/workers/search.709b4209.min.js b/assets/javascripts/workers/search.709b4209.min.js old mode 100755 new mode 100644 diff --git a/assets/javascripts/workers/search.709b4209.min.js.map b/assets/javascripts/workers/search.709b4209.min.js.map old mode 100755 new mode 100644 diff --git a/assets/pnp_logo.png b/assets/pnp_logo.png old mode 100755 new mode 100644 diff --git a/assets/stylesheets/main.f7f47774.min.css b/assets/stylesheets/main.f7f47774.min.css old mode 100755 new mode 100644 diff --git a/assets/stylesheets/main.f7f47774.min.css.map b/assets/stylesheets/main.f7f47774.min.css.map old mode 100755 new mode 100644 diff --git a/assets/stylesheets/palette.3f5d1f46.min.css b/assets/stylesheets/palette.3f5d1f46.min.css old mode 100755 new mode 100644 diff --git a/assets/stylesheets/palette.3f5d1f46.min.css.map b/assets/stylesheets/palette.3f5d1f46.min.css.map old mode 100755 new mode 100644 diff --git a/assets/theme_debug.png b/assets/theme_debug.png old mode 100755 new mode 100644 diff --git a/assets/theme_field.png b/assets/theme_field.png old mode 100755 new mode 100644 diff --git a/assets/using-query-tools/PeopleSearch.png b/assets/using-query-tools/PeopleSearch.png old mode 100755 new mode 100644 diff --git a/assets/using-query-tools/ReindexLibrary.png b/assets/using-query-tools/ReindexLibrary.png old mode 100755 new mode 100644 diff --git a/assets/using-query-tools/rightQuery1.png b/assets/using-query-tools/rightQuery1.png old mode 100755 new mode 100644 diff --git a/assets/using-query-tools/wrongQuery1.png b/assets/using-query-tools/wrongQuery1.png old mode 100755 new mode 100644 diff --git a/assets/v3_v4.png b/assets/v3_v4.png old mode 100755 new mode 100644 diff --git a/assets/webparts.png b/assets/webparts.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-box/dynamic_data_source.png b/assets/webparts/search-box/dynamic_data_source.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-box/search_box_settings.png b/assets/webparts/search-box/search_box_settings.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-box/search_box_suggestions.png b/assets/webparts/search-box/search_box_suggestions.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-box/search_box_wp_default.png b/assets/webparts/search-box/search_box_wp_default.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-box/search_box_wp_picker.png b/assets/webparts/search-box/search_box_wp_picker.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-box/suggestions_demo.png b/assets/webparts/search-box/suggestions_demo.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-box/suggestions_providers_panel.png b/assets/webparts/search-box/suggestions_providers_panel.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-filters/checkbox_template.png b/assets/webparts/search-filters/checkbox_template.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-filters/combo_template.png b/assets/webparts/search-filters/combo_template.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-filters/dateinterval_template.png b/assets/webparts/search-filters/dateinterval_template.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-filters/daterange_template.png b/assets/webparts/search-filters/daterange_template.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-filters/filter_deep_linking.png b/assets/webparts/search-filters/filter_deep_linking.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-filters/filter_settings.png b/assets/webparts/search-filters/filter_settings.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-filters/filter_settings_panel.png b/assets/webparts/search-filters/filter_settings_panel.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-filters/layouts/available_layouts.png b/assets/webparts/search-filters/layouts/available_layouts.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-filters/layouts/custom_edit.png b/assets/webparts/search-filters/layouts/custom_edit.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-filters/layouts/custom_external_file.png b/assets/webparts/search-filters/layouts/custom_external_file.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-filters/layouts/custom_layout.png b/assets/webparts/search-filters/layouts/custom_layout.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-filters/layouts/debug_layout.png b/assets/webparts/search-filters/layouts/debug_layout.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-filters/layouts/horizontal_layout.png b/assets/webparts/search-filters/layouts/horizontal_layout.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-filters/layouts/horizontal_options.png b/assets/webparts/search-filters/layouts/horizontal_options.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-filters/layouts/panel_layout.png b/assets/webparts/search-filters/layouts/panel_layout.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-filters/layouts/panel_options.png b/assets/webparts/search-filters/layouts/panel_options.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-filters/layouts/vertical_layout.png b/assets/webparts/search-filters/layouts/vertical_layout.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-filters/operator_filters.png b/assets/webparts/search-filters/operator_filters.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-filters/search_filters_wp_default.png b/assets/webparts/search-filters/search_filters_wp_default.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-filters/search_filters_wp_picker.png b/assets/webparts/search-filters/search_filters_wp_picker.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-filters/wp_connection.png b/assets/webparts/search-filters/wp_connection.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/about_page.png b/assets/webparts/search-results/about_page.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/connections/allow_item_selection.png b/assets/webparts/search-results/connections/allow_item_selection.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/connections/available_connections.png b/assets/webparts/search-results/connections/available_connections.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/connections/configure_data_results_connection_1.png b/assets/webparts/search-results/connections/configure_data_results_connection_1.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/connections/configure_data_results_connection_2.png b/assets/webparts/search-results/connections/configure_data_results_connection_2.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/connections/configure_data_results_connection_3.png b/assets/webparts/search-results/connections/configure_data_results_connection_3.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/connections/configure_data_results_connection_4.png b/assets/webparts/search-results/connections/configure_data_results_connection_4.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/connections/configure_data_results_connection_5.png b/assets/webparts/search-results/connections/configure_data_results_connection_5.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/connections/filters.png b/assets/webparts/search-results/connections/filters.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/connections/input_text_dynamic.png b/assets/webparts/search-results/connections/input_text_dynamic.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/connections/input_text_static.png b/assets/webparts/search-results/connections/input_text_static.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/connections/select_vertical.png b/assets/webparts/search-results/connections/select_vertical.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/connections/verticals.png b/assets/webparts/search-results/connections/verticals.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/available_layouts.png b/assets/webparts/search-results/layouts/available_layouts.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/card_allow_html.png b/assets/webparts/search-results/layouts/card_allow_html.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/card_preview.png b/assets/webparts/search-results/layouts/card_preview.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/cards_layout.png b/assets/webparts/search-results/layouts/cards_layout.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/common_options.png b/assets/webparts/search-results/layouts/common_options.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/custom_edit.png b/assets/webparts/search-results/layouts/custom_edit.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/custom_external_file.png b/assets/webparts/search-results/layouts/custom_external_file.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/custom_layout.png b/assets/webparts/search-results/layouts/custom_layout.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/debug_layout.png b/assets/webparts/search-results/layouts/debug_layout.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/details_list_fields.png b/assets/webparts/search-results/layouts/details_list_fields.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/details_list_hb_expr.png b/assets/webparts/search-results/layouts/details_list_hb_expr.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/details_list_hb_expr2.png b/assets/webparts/search-results/layouts/details_list_hb_expr2.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/details_list_layout.png b/assets/webparts/search-results/layouts/details_list_layout.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/hide_webpart.png b/assets/webparts/search-results/layouts/hide_webpart.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/manage_cards_fields.png b/assets/webparts/search-results/layouts/manage_cards_fields.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/manage_people_fields.png b/assets/webparts/search-results/layouts/manage_people_fields.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/people_layout.png b/assets/webparts/search-results/layouts/people_layout.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/persona_card_hover.png b/assets/webparts/search-results/layouts/persona_card_hover.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/result_types.png b/assets/webparts/search-results/layouts/result_types.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/result_types_btn.png b/assets/webparts/search-results/layouts/result_types_btn.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/result_types_result.png b/assets/webparts/search-results/layouts/result_types_result.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/result_types_template.png b/assets/webparts/search-results/layouts/result_types_template.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/see_all_link.png b/assets/webparts/search-results/layouts/see_all_link.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/simple_list_layout.png b/assets/webparts/search-results/layouts/simple_list_layout.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/simple_list_thumbnail.png b/assets/webparts/search-results/layouts/simple_list_thumbnail.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/layouts/slider_layout.png b/assets/webparts/search-results/layouts/slider_layout.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/localization_crawled_property.png b/assets/webparts/search-results/localization_crawled_property.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/page1/available_datasources.png b/assets/webparts/search-results/page1/available_datasources.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/page1/custom_slot.png b/assets/webparts/search-results/page1/custom_slot.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/page1/data_source_slots.png b/assets/webparts/search-results/page1/data_source_slots.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/page1/slot_concept.png b/assets/webparts/search-results/page1/slot_concept.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/search_results_wp_picker.png b/assets/webparts/search-results/search_results_wp_picker.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-results/search_results_wp_placeholder.png b/assets/webparts/search-results/search_results_wp_placeholder.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-verticals/configure_verticals.png b/assets/webparts/search-verticals/configure_verticals.png old mode 100755 new mode 100644 diff --git a/assets/webparts/search-verticals/search_verticals_wp_picker.png b/assets/webparts/search-verticals/search_verticals_wp_picker.png old mode 100755 new mode 100644 diff --git a/build-the-doc/index.html b/build-the-doc/index.html old mode 100755 new mode 100644 diff --git a/extensibility/adaptivecards_customizations/index.html b/extensibility/adaptivecards_customizations/index.html old mode 100755 new mode 100644 diff --git a/extensibility/custom_data_sources/index.html b/extensibility/custom_data_sources/index.html old mode 100755 new mode 100644 diff --git a/extensibility/custom_layout/index.html b/extensibility/custom_layout/index.html old mode 100755 new mode 100644 diff --git a/extensibility/custom_query_modifications/index.html b/extensibility/custom_query_modifications/index.html old mode 100755 new mode 100644 diff --git a/extensibility/custom_suggestions_provider/index.html b/extensibility/custom_suggestions_provider/index.html old mode 100755 new mode 100644 diff --git a/extensibility/custom_web_component/index.html b/extensibility/custom_web_component/index.html old mode 100755 new mode 100644 diff --git a/extensibility/handlebars_customizations/index.html b/extensibility/handlebars_customizations/index.html old mode 100755 new mode 100644 index f664a88b8..e2aeb6f76 --- a/extensibility/handlebars_customizations/index.html +++ b/extensibility/handlebars_customizations/index.html @@ -911,6 +911,9 @@

Register Handlebars customizations }); } +
    +
  1. To reference the deployed manifest id of your extension in the search web part see the Introduction.
  2. +
diff --git a/extensibility/index.html b/extensibility/index.html old mode 100755 new mode 100644 diff --git a/extensibility/templating/index.html b/extensibility/templating/index.html old mode 100755 new mode 100644 diff --git a/extensibility/web_components_list/index.html b/extensibility/web_components_list/index.html old mode 100755 new mode 100644 diff --git a/how-to-contribute/index.html b/how-to-contribute/index.html old mode 100755 new mode 100644 diff --git a/index.html b/index.html old mode 100755 new mode 100644 index 80691ee74..f63403714 --- a/index.html +++ b/index.html @@ -1111,17 +1111,17 @@

About&

Maintainers & contributors

Here is the list of main contributors of the PnP Modern Search (all versions included)

diff --git a/installation/index.html b/installation/index.html old mode 100755 new mode 100644 diff --git a/scenarios/Create-a-search-page-with-verticals-on-different-pages/index.html b/scenarios/Create-a-search-page-with-verticals-on-different-pages/index.html old mode 100755 new mode 100644 diff --git a/scenarios/Create-a-search-page-with-verticals-within-the-same-page/index.html b/scenarios/Create-a-search-page-with-verticals-within-the-same-page/index.html old mode 100755 new mode 100644 diff --git a/scenarios/Create-a-useful-People-Search/index.html b/scenarios/Create-a-useful-People-Search/index.html old mode 100755 new mode 100644 diff --git a/scenarios/Use-query-rules-for-promoted-links/index.html b/scenarios/Use-query-rules-for-promoted-links/index.html old mode 100755 new mode 100644 diff --git a/scenarios/assets/Create-a-search-page-with-verticals-on-different-pages/add-PnP-web-parts.png b/scenarios/assets/Create-a-search-page-with-verticals-on-different-pages/add-PnP-web-parts.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Create-a-search-page-with-verticals-on-different-pages/configure-search-verticals-on-first-page.png b/scenarios/assets/Create-a-search-page-with-verticals-on-different-pages/configure-search-verticals-on-first-page.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Create-a-search-page-with-verticals-on-different-pages/configure-search-verticals-on-second-page.png b/scenarios/assets/Create-a-search-page-with-verticals-on-different-pages/configure-search-verticals-on-second-page.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Create-a-search-page-with-verticals-on-different-pages/create-a-page.png b/scenarios/assets/Create-a-search-page-with-verticals-on-different-pages/create-a-page.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Create-a-search-page-with-verticals-on-different-pages/first-result-web-part-connection.png b/scenarios/assets/Create-a-search-page-with-verticals-on-different-pages/first-result-web-part-connection.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Create-a-search-page-with-verticals-on-different-pages/first-result-web-part-vertical-everything.png b/scenarios/assets/Create-a-search-page-with-verticals-on-different-pages/first-result-web-part-vertical-everything.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Create-a-search-page-with-verticals-on-different-pages/search-box-configuration.png b/scenarios/assets/Create-a-search-page-with-verticals-on-different-pages/search-box-configuration.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Create-a-search-page-with-verticals-on-different-pages/search-vertical-on-first-page.png b/scenarios/assets/Create-a-search-page-with-verticals-on-different-pages/search-vertical-on-first-page.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Create-a-search-page-with-verticals-on-different-pages/search-vertical-on-second-page.png b/scenarios/assets/Create-a-search-page-with-verticals-on-different-pages/search-vertical-on-second-page.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Create-a-search-page-with-verticals-on-different-pages/second-result-web-part-connection.png b/scenarios/assets/Create-a-search-page-with-verticals-on-different-pages/second-result-web-part-connection.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Create-a-search-page-with-verticals-on-different-pages/second-result-web-part-vertical-videos.png b/scenarios/assets/Create-a-search-page-with-verticals-on-different-pages/second-result-web-part-vertical-videos.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Create-a-search-page-with-verticals-on-different-pages/used-search-web-parts.png b/scenarios/assets/Create-a-search-page-with-verticals-on-different-pages/used-search-web-parts.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Create-a-search-page-with-verticals-within-the-same-page/Add-PnP-Web-Parts.png b/scenarios/assets/Create-a-search-page-with-verticals-within-the-same-page/Add-PnP-Web-Parts.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Create-a-search-page-with-verticals-within-the-same-page/Configure-Search-Box-Web-Part.png b/scenarios/assets/Create-a-search-page-with-verticals-within-the-same-page/Configure-Search-Box-Web-Part.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Create-a-search-page-with-verticals-within-the-same-page/Configure-Search-Vertical-Web-Part.png b/scenarios/assets/Create-a-search-page-with-verticals-within-the-same-page/Configure-Search-Vertical-Web-Part.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Create-a-search-page-with-verticals-within-the-same-page/Create-a-page.png b/scenarios/assets/Create-a-search-page-with-verticals-within-the-same-page/Create-a-page.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Create-a-search-page-with-verticals-within-the-same-page/First-Result-Web-Part-Vertical-Everything.png b/scenarios/assets/Create-a-search-page-with-verticals-within-the-same-page/First-Result-Web-Part-Vertical-Everything.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Create-a-search-page-with-verticals-within-the-same-page/First-Result-Web-Part-connection.png b/scenarios/assets/Create-a-search-page-with-verticals-within-the-same-page/First-Result-Web-Part-connection.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Create-a-search-page-with-verticals-within-the-same-page/Search-Vertical.png b/scenarios/assets/Create-a-search-page-with-verticals-within-the-same-page/Search-Vertical.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Create-a-search-page-with-verticals-within-the-same-page/Search-Web-Parts.png b/scenarios/assets/Create-a-search-page-with-verticals-within-the-same-page/Search-Web-Parts.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Create-a-search-page-with-verticals-within-the-same-page/Second-Result-Web-Part-Vertical-Knowledge.png b/scenarios/assets/Create-a-search-page-with-verticals-within-the-same-page/Second-Result-Web-Part-Vertical-Knowledge.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Create-a-search-page-with-verticals-within-the-same-page/Second-Result-Web-Part-connection.png b/scenarios/assets/Create-a-search-page-with-verticals-within-the-same-page/Second-Result-Web-Part-connection.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Create-a-search-page-with-verticals-within-the-same-page/Test-Your-Search-page.png b/scenarios/assets/Create-a-search-page-with-verticals-within-the-same-page/Test-Your-Search-page.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Use-query-rules-for-promoted-links/Create-a-page.png b/scenarios/assets/Use-query-rules-for-promoted-links/Create-a-page.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Use-query-rules-for-promoted-links/add-promoted-result.png b/scenarios/assets/Use-query-rules-for-promoted-links/add-promoted-result.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Use-query-rules-for-promoted-links/add-webparts.png b/scenarios/assets/Use-query-rules-for-promoted-links/add-webparts.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Use-query-rules-for-promoted-links/configure-search-results.png b/scenarios/assets/Use-query-rules-for-promoted-links/configure-search-results.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Use-query-rules-for-promoted-links/configured-query-rule.png b/scenarios/assets/Use-query-rules-for-promoted-links/configured-query-rule.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Use-query-rules-for-promoted-links/connection-search-box.png b/scenarios/assets/Use-query-rules-for-promoted-links/connection-search-box.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Use-query-rules-for-promoted-links/create-a-new-query-rule.png b/scenarios/assets/Use-query-rules-for-promoted-links/create-a-new-query-rule.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Use-query-rules-for-promoted-links/query-rule-information.png b/scenarios/assets/Use-query-rules-for-promoted-links/query-rule-information.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Use-query-rules-for-promoted-links/result-webpart-enable-query-rules.png b/scenarios/assets/Use-query-rules-for-promoted-links/result-webpart-enable-query-rules.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/Use-query-rules-for-promoted-links/show-promoted-links.png b/scenarios/assets/Use-query-rules-for-promoted-links/show-promoted-links.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/create-a-useful-people-search/Add-PnP-Web-Parts.png b/scenarios/assets/create-a-useful-people-search/Add-PnP-Web-Parts.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/create-a-useful-people-search/Create-a-page.png b/scenarios/assets/create-a-useful-people-search/Create-a-page.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/create-a-useful-people-search/arrange-your-search-web-parts.png b/scenarios/assets/create-a-useful-people-search/arrange-your-search-web-parts.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/create-a-useful-people-search/configure-Search-Box.png b/scenarios/assets/create-a-useful-people-search/configure-Search-Box.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/create-a-useful-people-search/configure-customize-filters.png b/scenarios/assets/create-a-useful-people-search/configure-customize-filters.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/create-a-useful-people-search/configure-search-filters.png b/scenarios/assets/create-a-useful-people-search/configure-search-filters.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/create-a-useful-people-search/configure-search-results.png b/scenarios/assets/create-a-useful-people-search/configure-search-results.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/create-a-useful-people-search/people-search.png b/scenarios/assets/create-a-useful-people-search/people-search.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/create-a-useful-people-search/search-results-connections.png b/scenarios/assets/create-a-useful-people-search/search-results-connections.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/create-a-useful-people-search/search-results-layout-options.png b/scenarios/assets/create-a-useful-people-search/search-results-layout-options.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/create-a-useful-people-search/search-results-layouts.png b/scenarios/assets/create-a-useful-people-search/search-results-layouts.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/create-simple-search-page/add-web-parts-to-page.jpg b/scenarios/assets/create-simple-search-page/add-web-parts-to-page.jpg old mode 100755 new mode 100644 diff --git a/scenarios/assets/create-simple-search-page/available-data-sources.jpg b/scenarios/assets/create-simple-search-page/available-data-sources.jpg old mode 100755 new mode 100644 diff --git a/scenarios/assets/create-simple-search-page/new-page.jpg b/scenarios/assets/create-simple-search-page/new-page.jpg old mode 100755 new mode 100644 diff --git a/scenarios/assets/create-simple-search-page/use-input-query-text.jpg b/scenarios/assets/create-simple-search-page/use-input-query-text.jpg old mode 100755 new mode 100644 diff --git a/scenarios/assets/create-simple-search-page/web-part-toolbox-pnp-modern-search.jpg b/scenarios/assets/create-simple-search-page/web-part-toolbox-pnp-modern-search.jpg old mode 100755 new mode 100644 diff --git a/scenarios/assets/create-your-first-custom-template/custom_search-results-example.png b/scenarios/assets/create-your-first-custom-template/custom_search-results-example.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/create-your-first-custom-template/insert_external-url-for-custom-template.png b/scenarios/assets/create-your-first-custom-template/insert_external-url-for-custom-template.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/create-your-first-custom-template/myfirsttemplate_file-screenshot.png b/scenarios/assets/create-your-first-custom-template/myfirsttemplate_file-screenshot.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/edit-custom-templates-in-sharepoint/open_htmlfile-in-text-editor.png b/scenarios/assets/edit-custom-templates-in-sharepoint/open_htmlfile-in-text-editor.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/edit-custom-templates-in-sharepoint/save_the-changed-file.png b/scenarios/assets/edit-custom-templates-in-sharepoint/save_the-changed-file.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/edit-templates-using-vscode-and-onedrive/open-template-folder-onedrive.png b/scenarios/assets/edit-templates-using-vscode-and-onedrive/open-template-folder-onedrive.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/edit-templates-using-vscode-and-onedrive/refresh-search-page-and-see-result-of-changes.png b/scenarios/assets/edit-templates-using-vscode-and-onedrive/refresh-search-page-and-see-result-of-changes.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/edit-templates-using-vscode-and-onedrive/sync-or-addshortcut-to-onedrive.png b/scenarios/assets/edit-templates-using-vscode-and-onedrive/sync-or-addshortcut-to-onedrive.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/page-with-filters/connect-filter-web-part.png b/scenarios/assets/page-with-filters/connect-filter-web-part.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/page-with-filters/connect-search-results-web-part.png b/scenarios/assets/page-with-filters/connect-search-results-web-part.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/page-with-filters/page-layout-with-filters.png b/scenarios/assets/page-with-filters/page-layout-with-filters.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/page-with-filters/selected-properties.png b/scenarios/assets/page-with-filters/selected-properties.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/page-with-filters/set-up-filters-properties.png b/scenarios/assets/page-with-filters/set-up-filters-properties.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/page-with-filters/set-up-filters.png b/scenarios/assets/page-with-filters/set-up-filters.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/store-custom-templates-in-sharepoint/central-repository-multiple-sites.png b/scenarios/assets/store-custom-templates-in-sharepoint/central-repository-multiple-sites.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/store-custom-templates-in-sharepoint/custom-template-external-url-edit.png b/scenarios/assets/store-custom-templates-in-sharepoint/custom-template-external-url-edit.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/store-custom-templates-in-sharepoint/sharepoint-resources-lib.png b/scenarios/assets/store-custom-templates-in-sharepoint/sharepoint-resources-lib.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/use-query-string-in-url/Create-a-page.png b/scenarios/assets/use-query-string-in-url/Create-a-page.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/use-query-string-in-url/add-web-parts.png b/scenarios/assets/use-query-string-in-url/add-web-parts.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/use-query-string-in-url/configure-search-results-query.png b/scenarios/assets/use-query-string-in-url/configure-search-results-query.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/use-query-string-in-url/query-string-parameter.png b/scenarios/assets/use-query-string-in-url/query-string-parameter.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/use-query-string-in-url/query-template.png b/scenarios/assets/use-query-string-in-url/query-template.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/use-query-string-in-url/quicklinks-query-string.png b/scenarios/assets/use-query-string-in-url/quicklinks-query-string.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/use-query-string-in-url/readme b/scenarios/assets/use-query-string-in-url/readme old mode 100755 new mode 100644 index d3f5a12fa..8b1378917 --- a/scenarios/assets/use-query-string-in-url/readme +++ b/scenarios/assets/use-query-string-in-url/readme @@ -1 +1 @@ - + diff --git a/scenarios/assets/use-query-string-in-url/solution.png b/scenarios/assets/use-query-string-in-url/solution.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/use-search-as-a-department-webpart/Add-PnP-Results-Web-Parts.png b/scenarios/assets/use-search-as-a-department-webpart/Add-PnP-Results-Web-Parts.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/use-search-as-a-department-webpart/Create-a-page.png b/scenarios/assets/use-search-as-a-department-webpart/Create-a-page.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/use-search-as-a-department-webpart/Layout1.png b/scenarios/assets/use-search-as-a-department-webpart/Layout1.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/use-search-as-a-department-webpart/Layout2.png b/scenarios/assets/use-search-as-a-department-webpart/Layout2.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/use-search-as-a-department-webpart/MarketingDepartment.png b/scenarios/assets/use-search-as-a-department-webpart/MarketingDepartment.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/use-search-as-a-department-webpart/Setup-page1.png b/scenarios/assets/use-search-as-a-department-webpart/Setup-page1.png old mode 100755 new mode 100644 diff --git a/scenarios/assets/use-search-as-a-department-webpart/Termstore_marketing.png b/scenarios/assets/use-search-as-a-department-webpart/Termstore_marketing.png old mode 100755 new mode 100644 diff --git a/scenarios/create-simple-search-page/index.html b/scenarios/create-simple-search-page/index.html old mode 100755 new mode 100644 diff --git a/scenarios/create-your-first-custom-template/index.html b/scenarios/create-your-first-custom-template/index.html old mode 100755 new mode 100644 diff --git a/scenarios/edit-custom-templates-in-sharepoint/index.html b/scenarios/edit-custom-templates-in-sharepoint/index.html old mode 100755 new mode 100644 diff --git a/scenarios/edit-templates-using-vscode-and-onedrive/index.html b/scenarios/edit-templates-using-vscode-and-onedrive/index.html old mode 100755 new mode 100644 diff --git a/scenarios/howto-store-custom-templates-in-sharepoint/index.html b/scenarios/howto-store-custom-templates-in-sharepoint/index.html old mode 100755 new mode 100644 diff --git a/scenarios/includes/deployment-note/index.html b/scenarios/includes/deployment-note/index.html old mode 100755 new mode 100644 diff --git a/scenarios/index.html b/scenarios/index.html old mode 100755 new mode 100644 diff --git a/scenarios/page-with-filters/index.html b/scenarios/page-with-filters/index.html old mode 100755 new mode 100644 diff --git a/scenarios/set-up-managed-properties/index.html b/scenarios/set-up-managed-properties/index.html old mode 100755 new mode 100644 diff --git a/scenarios/use-query-string-in-url/index.html b/scenarios/use-query-string-in-url/index.html old mode 100755 new mode 100644 diff --git a/scenarios/use-search-as-a-department-webpart/index.html b/scenarios/use-search-as-a-department-webpart/index.html old mode 100755 new mode 100644 diff --git a/search/search_index.json b/search/search_index.json old mode 100755 new mode 100644 index 3de22c62d..7ff83597d --- a/search/search_index.json +++ b/search/search_index.json @@ -1 +1 @@ -{"config":{"indexing":"full","lang":["en"],"min_search_length":3,"prebuild_index":false,"separator":"[\\s\\-]+"},"docs":[{"location":"","text":"PnP Modern Search v4 \u00b6 The PnP 'Modern Search' solution is a set of SharePoint Online modern Web Parts allowing SharePoint super users, webmasters and developers to create highly flexible and personalized search based experiences in minutes. Before modern pages and web parts built on SPFx was introduced search driven scenarios was covered by the highly flexible classic search web parts, which supported any developer to add any HTML, CSS or JavaScript they wanted to tailor their specific scenario. In the modern world this was replaced by the Highlighted Content Web Part and a not very configurable search solution for Microsoft Search. To close the gap of customization and freedom the PnP Modern Search web parts got stated back in 2017, and have stabilized on v3. While allowing flexibility it introduces security measures to block JavaScript and CSS injection, key to many of the enterprise companies using the web parts today in productions. As the project progressed and the search API's are moving from SharePoint to Microsoft Graph there was a need to restructure and re-invent the web parts. Hence v4 was born. The goal of v4 is to solve scenarios already solved by v3, but at the same time allow greater flexibility in how you extend the solution using web components and custom developer solutions outside of HTML/handlebars. As more and more Microsoft Search functionality is exposed via the Microsoft Graph Search API's, we will keep on investing in v4 to surface these great capabilities. Looking for the v3 documentation? Here you go! PnP Modern Search v3.x deprecation v4 uses a brand new code architecture and replace the older v3 codebase . There will be no new features added to v3.x, but we will continue to provide bug fixes and minor changes as needed. As v4.x is not yet at feature parity with v3.x, you can still use the v3.x packages to meet your requirements. Also not that there is not an auto-upgrade path from v3 to v4 due to the new architecture, so you are perfectly ok to stay on the v3 version until v4 provides the features validating your upgrade. However, the main focus is on the v4 version, and new search functionality backed by the Microsoft Graph Search API will be v4 only. v3 and v4 don't share the same package name, Web Part and solution IDs meaning you can have them side by side on a page if necessary without overlap. What's included? \u00b6 The solution includes the following Web Parts: Component Description Search Results Retrieve data from a data source and render them in a specific layout. Search Filters Filter and refine data displayed in 'Search Results' Web Parts. Search Verticals Browse data as silos (i.e. tabs) from multiple data sources. Search box Let users enter free text queries sent to 'Search Results' Web Parts. Supported browsers \u00b6 Here is the list of supported browsers: Chrome Firefox Edge Edge Chromium Brave PnP Modern Search do not explicitly support Internet Explorer 11 . We think there are plenty of other options for enterprise scenarios in the market. Maybe it's time to move on. For developers, it represents an huge amount of time to make the solution compatible for a very low benefit. Hope you understand, ain't personal ;). Extensibility model \u00b6 By getting this solution, you also benefit from an advanced extensibility model allowing you to customize the solution according to your requirements if default features don't do the job for you. The supported extensions are: Custom layouts . Custom web components . Custom Handlebars customization (helpers, partials, etc.) . Custom event handlers for adaptive cards actions . Custom query modifiers . Custom data sources . Custom suggestions providers . With these available customizations options, you can do pretty much anything! Note Extensibility samples are centralized in a dedicated repository: https://github.com/microsoft-search/pnp-modern-search-extensibility-samples/tree/main . Use them to get started to create your own or reuse existing samples in your projects. Troubleshooting \u00b6 If you encounter an issue, please use the GitHub issues list of this repository . However, we will ask you to verify your issue as described here: Using Query tools to verify issues Also, to help us to resolve your issue, you can include screenshots or error messages coming from: The faulty Web Part itself. Errors displayed in the browser console (typically pressing F12). Errors displayed in the SharePoint console (pressing CTRL+F12) Issues, questions, feedback? \u00b6 For any issue, question or feedback, please the official GitHub repository . We will be happy to help you! About \u00b6 PnP Modern Search version 4 initially made by Franck Cornu based on a fork of the @aequos 'Modern Data Visualizer' solution. Maintainers & contributors \u00b6 Here is the list of main contributors of the PnP Modern Search (all versions included) Franck Cornu (aequos) - @FranckCornu Mikael Svenson (Microsoft) - @mikaelsvenson Yannick Reekmans - @yannickreekmans Albert-Jan Schot - @appieschot Tarald G\u00e5sbakk (PuzzlePart) - @taraldgasbakk Brad Schlintz (Microsoft) - @bschlintz Richard Gigan - @PooLP Matthew Stark Fabio Franzini (Apvee) - @franzinifabio Paolo Pialorsi (PiaSys.com) - @PaoloPia Patrtik Hellgren (SherparsGroupAB) - @PatrikHellgren Erfan Darroudi @edarroudi","title":"Introduction"},{"location":"#pnp-modern-search-v4","text":"The PnP 'Modern Search' solution is a set of SharePoint Online modern Web Parts allowing SharePoint super users, webmasters and developers to create highly flexible and personalized search based experiences in minutes. Before modern pages and web parts built on SPFx was introduced search driven scenarios was covered by the highly flexible classic search web parts, which supported any developer to add any HTML, CSS or JavaScript they wanted to tailor their specific scenario. In the modern world this was replaced by the Highlighted Content Web Part and a not very configurable search solution for Microsoft Search. To close the gap of customization and freedom the PnP Modern Search web parts got stated back in 2017, and have stabilized on v3. While allowing flexibility it introduces security measures to block JavaScript and CSS injection, key to many of the enterprise companies using the web parts today in productions. As the project progressed and the search API's are moving from SharePoint to Microsoft Graph there was a need to restructure and re-invent the web parts. Hence v4 was born. The goal of v4 is to solve scenarios already solved by v3, but at the same time allow greater flexibility in how you extend the solution using web components and custom developer solutions outside of HTML/handlebars. As more and more Microsoft Search functionality is exposed via the Microsoft Graph Search API's, we will keep on investing in v4 to surface these great capabilities. Looking for the v3 documentation? Here you go! PnP Modern Search v3.x deprecation v4 uses a brand new code architecture and replace the older v3 codebase . There will be no new features added to v3.x, but we will continue to provide bug fixes and minor changes as needed. As v4.x is not yet at feature parity with v3.x, you can still use the v3.x packages to meet your requirements. Also not that there is not an auto-upgrade path from v3 to v4 due to the new architecture, so you are perfectly ok to stay on the v3 version until v4 provides the features validating your upgrade. However, the main focus is on the v4 version, and new search functionality backed by the Microsoft Graph Search API will be v4 only. v3 and v4 don't share the same package name, Web Part and solution IDs meaning you can have them side by side on a page if necessary without overlap.","title":"PnP Modern Search v4"},{"location":"#whats-included","text":"The solution includes the following Web Parts: Component Description Search Results Retrieve data from a data source and render them in a specific layout. Search Filters Filter and refine data displayed in 'Search Results' Web Parts. Search Verticals Browse data as silos (i.e. tabs) from multiple data sources. Search box Let users enter free text queries sent to 'Search Results' Web Parts.","title":"What's included?"},{"location":"#supported-browsers","text":"Here is the list of supported browsers: Chrome Firefox Edge Edge Chromium Brave PnP Modern Search do not explicitly support Internet Explorer 11 . We think there are plenty of other options for enterprise scenarios in the market. Maybe it's time to move on. For developers, it represents an huge amount of time to make the solution compatible for a very low benefit. Hope you understand, ain't personal ;).","title":"Supported browsers"},{"location":"#extensibility-model","text":"By getting this solution, you also benefit from an advanced extensibility model allowing you to customize the solution according to your requirements if default features don't do the job for you. The supported extensions are: Custom layouts . Custom web components . Custom Handlebars customization (helpers, partials, etc.) . Custom event handlers for adaptive cards actions . Custom query modifiers . Custom data sources . Custom suggestions providers . With these available customizations options, you can do pretty much anything! Note Extensibility samples are centralized in a dedicated repository: https://github.com/microsoft-search/pnp-modern-search-extensibility-samples/tree/main . Use them to get started to create your own or reuse existing samples in your projects.","title":"Extensibility model"},{"location":"#troubleshooting","text":"If you encounter an issue, please use the GitHub issues list of this repository . However, we will ask you to verify your issue as described here: Using Query tools to verify issues Also, to help us to resolve your issue, you can include screenshots or error messages coming from: The faulty Web Part itself. Errors displayed in the browser console (typically pressing F12). Errors displayed in the SharePoint console (pressing CTRL+F12)","title":"Troubleshooting"},{"location":"#issues-questions-feedback","text":"For any issue, question or feedback, please the official GitHub repository . We will be happy to help you!","title":"Issues, questions, feedback?"},{"location":"#about","text":"PnP Modern Search version 4 initially made by Franck Cornu based on a fork of the @aequos 'Modern Data Visualizer' solution.","title":"About"},{"location":"#maintainers-contributors","text":"Here is the list of main contributors of the PnP Modern Search (all versions included) Franck Cornu (aequos) - @FranckCornu Mikael Svenson (Microsoft) - @mikaelsvenson Yannick Reekmans - @yannickreekmans Albert-Jan Schot - @appieschot Tarald G\u00e5sbakk (PuzzlePart) - @taraldgasbakk Brad Schlintz (Microsoft) - @bschlintz Richard Gigan - @PooLP Matthew Stark Fabio Franzini (Apvee) - @franzinifabio Paolo Pialorsi (PiaSys.com) - @PaoloPia Patrtik Hellgren (SherparsGroupAB) - @PatrikHellgren Erfan Darroudi @edarroudi","title":"Maintainers & contributors"},{"location":"build-the-doc/","text":"Building the Documentation \u00b6 Building the documentation locally can help you visualize change you are making to the docs. What you see locally should be what you see online. Building \u00b6 Documentation is built using MkDocs. You will need to latest version of Python (tested on version 3.7.1) and pip. If you're on the Windows operating system, make sure you have added Python to your Path environment variable . When executing the pip module on Windows you can prefix it with python -m . For example: python -m pip install mkdocs-material Install MkDocs pip install mkdocs==1.2.2 pip install markdown-include Install the Material theme pip install mkdocs-material==7.2.4 Serve it up mkdocs serve Open a browser to http://127.0.0.1:8000/ Deploy mkdocs gh-deploy from main branch","title":"Building documentation"},{"location":"build-the-doc/#building-the-documentation","text":"Building the documentation locally can help you visualize change you are making to the docs. What you see locally should be what you see online.","title":"Building the Documentation"},{"location":"build-the-doc/#building","text":"Documentation is built using MkDocs. You will need to latest version of Python (tested on version 3.7.1) and pip. If you're on the Windows operating system, make sure you have added Python to your Path environment variable . When executing the pip module on Windows you can prefix it with python -m . For example: python -m pip install mkdocs-material Install MkDocs pip install mkdocs==1.2.2 pip install markdown-include Install the Material theme pip install mkdocs-material==7.2.4 Serve it up mkdocs serve Open a browser to http://127.0.0.1:8000/ Deploy mkdocs gh-deploy from main branch","title":"Building"},{"location":"how-to-contribute/","text":"How to contribute? \u00b6 You can contribute to this project at multiple levels: Help us with the issues list by: Answer questions from the community Fix issues in the code Improve documentation by: Correcting typos Clarify configuration and examples Add business scenario tutorials Add new reusable components , suggestions providers or Query modifier to the extensibility library. Add Web Part translations As a result, we accept pull requests from the community. You can refer to this post to learn how to make a PR on a GitHub repository. Note Your PR must target the develop branch. Important Your PR will be automatically rejected if It alters too much of the solution core architecture or the amount of code is too substantial to be reviewed properly. You don't provide any detailled steps to test it. It contains a new feature that was not discussed previously with the maintainers. Setting up the solution locally \u00b6 Before making any PR, you need to setup this project locally on your machine. This solution is composed of three distinct parts: Project Description search-parts SPFx Web Parts code search-extensibility SPFx library component containing shared code between core Web Parts and extensibilty library. search-extensibility-demo Reusable components to extend capabilities of core Web Parts https://github.com/microsoft-search/pnp-modern-search-extensibility-samples. Setup the search-extensibility project \u00b6 Note By default, the search-parts and search-extensibility-demo projects use the npm reference @pnp/modern-search-extensibility . Follow these steps only if you intend to perform some changes on the search-extensibility project. Important Because this project is published as an npm reference, any change is critical. Please do not commit changes if you are not sure about what you are doing. The search-extensibilty project is an SPFx library component containing all the shared interfaces for the search-parts and search-extensibility-demo other SPFx projects. As a result, a symbolic link must be build to these projects first before it can be used : Open the search-extensibility project and install dependencies using npm i or your favorite package manager. Build the project using the command npm run build or gulp bundle . Run the command npm link to create a symbolic link. You can also refer to the official SPFx documentation about library component usage . A symbolic link is a shortcut that points to another directory or another project (in this case) on your system Setup the search-parts and search-extensibility-demo projects \u00b6 From the search-parts or search-extensibility-demo project, run npm i . Build the project using npm run build or gulp bundle . Note If you made local changes on the search-extensibility project, after each npm i , you must link your local search-extensibility project using the command npm link @pnp/modern-search-extensibility Debug the solution \u00b6 From Visual Studio Code console or any other console, from the search-parts folder, use the npm run serve command to start the server. We use SPFx Fast Serve Tool from Sergei Sergeev to speed up development process. From Visual Studio Code, use the 'Hosted Workbench' debug configuration with your URL to debug the Web Parts. Any changes to the code will trigger a new build and refresh your page automatically within seconds.","title":"How to contribute?"},{"location":"how-to-contribute/#how-to-contribute","text":"You can contribute to this project at multiple levels: Help us with the issues list by: Answer questions from the community Fix issues in the code Improve documentation by: Correcting typos Clarify configuration and examples Add business scenario tutorials Add new reusable components , suggestions providers or Query modifier to the extensibility library. Add Web Part translations As a result, we accept pull requests from the community. You can refer to this post to learn how to make a PR on a GitHub repository. Note Your PR must target the develop branch. Important Your PR will be automatically rejected if It alters too much of the solution core architecture or the amount of code is too substantial to be reviewed properly. You don't provide any detailled steps to test it. It contains a new feature that was not discussed previously with the maintainers.","title":"How to contribute?"},{"location":"how-to-contribute/#setting-up-the-solution-locally","text":"Before making any PR, you need to setup this project locally on your machine. This solution is composed of three distinct parts: Project Description search-parts SPFx Web Parts code search-extensibility SPFx library component containing shared code between core Web Parts and extensibilty library. search-extensibility-demo Reusable components to extend capabilities of core Web Parts https://github.com/microsoft-search/pnp-modern-search-extensibility-samples.","title":"Setting up the solution locally"},{"location":"how-to-contribute/#setup-the-search-extensibility-project","text":"Note By default, the search-parts and search-extensibility-demo projects use the npm reference @pnp/modern-search-extensibility . Follow these steps only if you intend to perform some changes on the search-extensibility project. Important Because this project is published as an npm reference, any change is critical. Please do not commit changes if you are not sure about what you are doing. The search-extensibilty project is an SPFx library component containing all the shared interfaces for the search-parts and search-extensibility-demo other SPFx projects. As a result, a symbolic link must be build to these projects first before it can be used : Open the search-extensibility project and install dependencies using npm i or your favorite package manager. Build the project using the command npm run build or gulp bundle . Run the command npm link to create a symbolic link. You can also refer to the official SPFx documentation about library component usage . A symbolic link is a shortcut that points to another directory or another project (in this case) on your system","title":"Setup the search-extensibility project"},{"location":"how-to-contribute/#setup-the-search-parts-and-search-extensibility-demo-projects","text":"From the search-parts or search-extensibility-demo project, run npm i . Build the project using npm run build or gulp bundle . Note If you made local changes on the search-extensibility project, after each npm i , you must link your local search-extensibility project using the command npm link @pnp/modern-search-extensibility","title":"Setup the search-parts and search-extensibility-demo projects"},{"location":"how-to-contribute/#debug-the-solution","text":"From Visual Studio Code console or any other console, from the search-parts folder, use the npm run serve command to start the server. We use SPFx Fast Serve Tool from Sergei Sergeev to speed up development process. From Visual Studio Code, use the 'Hosted Workbench' debug configuration with your URL to debug the Web Parts. Any changes to the code will trigger a new build and refresh your page automatically within seconds.","title":"Debug the solution"},{"location":"installation/","text":"Installation \u00b6 Download the latest SharePoint Framework packages pnp-modern-search-parts.sppkg from the GitHub repository . Add pnp-modern-search-parts.sppkg to the global tenant app catalog or a site collection app catalog. If you don't have an app catalog, follow this procedure to create one. The packages are deployed in the general Office 365 CDN meaning we don't host any code . For the pnp-modern-search-parts.sppkg package, you can choose to make the solution available in all sites or force to install an app to the site every time. The solution asks the following API permissions by default to enhance the experience. These permissions are not mandatory . If you don't accept them, you will simply have less available features. You can approve scopes from the API Access screen in the SharePoint Admin Center: https://-admin.sharepoint.com/_layouts/15/online/AdminHome.aspx#/webApiPermissionManagement If you'd like more details on this step, please see the Approving Scopes section below. Requested API permission Used for User.Read The Microsoft Graph Toolkit persona card in the people layout. People.Read Same as above. Contacts.Read Same as above. User.Read.All Same as above. Files.Read.All Allow search for files using Graph API (Drive / Drive Items). Mail.Read Allow search for user's e-mail using Graph API (Messages). Calendars.Read Allow search for user's calendar appointments using Graph API (Events). Sites.Read.All Allow search for sites using Graph API (Sites / List Items). ExternalItem.Read.All Allow search for connector items using Graph API (External Items). Bookmark.Read.All Allow search for Bookmarks in Microsoft Search in your organization. Acronym.Read.All Allow search for Acronyms in Microsoft Search in your organization. Chat.Read Allow search for Teams messages. ChannelMessage.Read.All Same as above. Add the Web Parts to a SharePoint and start building! Approving Scopes \u00b6 You can approve the required scopes in the SharePoint Admin Center on the API Access page. When you visit that page, you will see any pending requests. The screenshot below shows the pending requests for the v4 solution. You'll need to approve each request one at a time. If you have questions about what the requested scopes mean and what permissions they provide, check the article Manage access to Azure AD-secured APIs . After you approve each request your view will be as shown in the screenshot below.","title":"Installation"},{"location":"installation/#installation","text":"Download the latest SharePoint Framework packages pnp-modern-search-parts.sppkg from the GitHub repository . Add pnp-modern-search-parts.sppkg to the global tenant app catalog or a site collection app catalog. If you don't have an app catalog, follow this procedure to create one. The packages are deployed in the general Office 365 CDN meaning we don't host any code . For the pnp-modern-search-parts.sppkg package, you can choose to make the solution available in all sites or force to install an app to the site every time. The solution asks the following API permissions by default to enhance the experience. These permissions are not mandatory . If you don't accept them, you will simply have less available features. You can approve scopes from the API Access screen in the SharePoint Admin Center: https://-admin.sharepoint.com/_layouts/15/online/AdminHome.aspx#/webApiPermissionManagement If you'd like more details on this step, please see the Approving Scopes section below. Requested API permission Used for User.Read The Microsoft Graph Toolkit persona card in the people layout. People.Read Same as above. Contacts.Read Same as above. User.Read.All Same as above. Files.Read.All Allow search for files using Graph API (Drive / Drive Items). Mail.Read Allow search for user's e-mail using Graph API (Messages). Calendars.Read Allow search for user's calendar appointments using Graph API (Events). Sites.Read.All Allow search for sites using Graph API (Sites / List Items). ExternalItem.Read.All Allow search for connector items using Graph API (External Items). Bookmark.Read.All Allow search for Bookmarks in Microsoft Search in your organization. Acronym.Read.All Allow search for Acronyms in Microsoft Search in your organization. Chat.Read Allow search for Teams messages. ChannelMessage.Read.All Same as above. Add the Web Parts to a SharePoint and start building!","title":"Installation"},{"location":"installation/#approving-scopes","text":"You can approve the required scopes in the SharePoint Admin Center on the API Access page. When you visit that page, you will see any pending requests. The screenshot below shows the pending requests for the v4 solution. You'll need to approve each request one at a time. If you have questions about what the requested scopes mean and what permissions they provide, check the article Manage access to Azure AD-secured APIs . After you approve each request your view will be as shown in the screenshot below.","title":"Approving Scopes"},{"location":"using-query-tools-to-verify-issues/","text":"Please verify your issue using these tools and methods before creating an Issue in the repository We DO value your questions and loves to see more and more people starting using PnP Modern Search, however we often see issues raised that has nothing to do with PnP Modern Search but the fact that the problem becomes visible here. The most common errors \u00b6 Typos or badly formed KQL queries Values not showing up on Managed Properties as expected Errors in mapping of Crawled Properties to Managed Properties ( especially for Refiners) Custom User Profile Properties are mapped incorrectly. We would therefore ask you to verify that the API delivers the results you are expecting before we starting looking for bugs in PnP Modern Search. Suggested tools \u00b6 SharePoint Search Query Tool (stand alone application) Guides for how to use the Query tool are available on the net, see for instance The Must Have Tool While Working with Search and SharePoint Online (Jasper Oosterveld) or Using SharePoint Search Query Tool (Antti Koskela) An older video is also availble: SharePoint Power Hour: Search Query Tool - YouTube (Laura Rogers) Chrome extention SP Editor . Guide: SharePoint Search Console \u2013 Now available inside Chrome SP Editor! (Antti Koskela) Video: SP Editor Chrome Extension for SharePoint Administrators and Developers (Denis Molodtsov) These tools gives you an exellent option to tinker with the search query and inspect the results. This will VERY often give you a clue where the issue is. Generic query errors \u00b6 In this case the query yields no result but you are certain that the name of the library is correct, what gives? Turns out that the Library has been renamed but the URL is still https://m365b839353.sharepoint.com/sites/NW-B2000eBike (note the dash) The Data is missing \u00b6 You set up a query on a few specific libraries and knows they contains 30 documents, but only 20 shows up, weird. There are a number of reasons: - One of the libraries is set as not to be searchable - Some of the documents have broken permissions and the user account you are using in the Search Tool doesn't have access. - and the dreaded: the documents haven't been indexed yet. The two first reasons are fairly easy to check and correct, but that last one is a bit tricky. The first step should be to prove or disprove the suspicion that the documents hasn't been indexed yet. One option is to use PnP PowerShell to query the CrawlLog, see Get-PnPSearchCrawlLog . Setting the -Filter to the URL of one of the missing douments should resolve that question. If the problem IS that the doucments haven't been indexed yet, you can request a reindexing, either on a List/Library level or on a Site level. A forced Full Index as known from On-Premises is not available in SharePoint Online. People Search \u00b6 Your company has added a new property to the User Properties in SharePoint and you are responsible for implementing it in search. You have found the creawled property and mapped it to a RefinableString in order to use it as a filter. You have waited the required 24 hours but the RefinableString is still not showing up. What is wrong? Most likely you have either forgotten or didn't knew that you MUST change the full-text-index from Default to PeopleIdx in your custom Managed Property and/or RefinableString otherwise it will show up in the wrong index, and be of no use. Follow this guide (SearchExplained) and you shouldn't encounter this problem anymore.","title":"Using query tools to verify issues"},{"location":"using-query-tools-to-verify-issues/#the-most-common-errors","text":"Typos or badly formed KQL queries Values not showing up on Managed Properties as expected Errors in mapping of Crawled Properties to Managed Properties ( especially for Refiners) Custom User Profile Properties are mapped incorrectly. We would therefore ask you to verify that the API delivers the results you are expecting before we starting looking for bugs in PnP Modern Search.","title":"The most common errors"},{"location":"using-query-tools-to-verify-issues/#suggested-tools","text":"","title":"Suggested tools"},{"location":"using-query-tools-to-verify-issues/#generic-query-errors","text":"In this case the query yields no result but you are certain that the name of the library is correct, what gives? Turns out that the Library has been renamed but the URL is still https://m365b839353.sharepoint.com/sites/NW-B2000eBike (note the dash)","title":"Generic query errors"},{"location":"using-query-tools-to-verify-issues/#the-data-is-missing","text":"You set up a query on a few specific libraries and knows they contains 30 documents, but only 20 shows up, weird. There are a number of reasons: - One of the libraries is set as not to be searchable - Some of the documents have broken permissions and the user account you are using in the Search Tool doesn't have access. - and the dreaded: the documents haven't been indexed yet. The two first reasons are fairly easy to check and correct, but that last one is a bit tricky. The first step should be to prove or disprove the suspicion that the documents hasn't been indexed yet. One option is to use PnP PowerShell to query the CrawlLog, see Get-PnPSearchCrawlLog . Setting the -Filter to the URL of one of the missing douments should resolve that question. If the problem IS that the doucments haven't been indexed yet, you can request a reindexing, either on a List/Library level or on a Site level. A forced Full Index as known from On-Premises is not available in SharePoint Online.","title":"The Data is missing"},{"location":"using-query-tools-to-verify-issues/#people-search","text":"Your company has added a new property to the User Properties in SharePoint and you are responsible for implementing it in search. You have found the creawled property and mapped it to a RefinableString in order to use it as a filter. You have waited the required 24 hours but the RefinableString is still not showing up. What is wrong? Most likely you have either forgotten or didn't knew that you MUST change the full-text-index from Default to PeopleIdx in your custom Managed Property and/or RefinableString otherwise it will show up in the wrong index, and be of no use. Follow this guide (SearchExplained) and you shouldn't encounter this problem anymore.","title":"People Search"},{"location":"extensibility/","text":"Extensibility possibilities \u00b6 This solution supports different levels of customizations depending your requirements: 'Basic' customizations : these include custom settings for data sources, search box, verticals and filters Web Parts + minor updates to existing layouts by adding custom HTML markup (ex: add a custom field in the UI from a data source), updates to builtin layouts fields ('Cards','Details List' and 'People'), etc. They only require HTML, CSS and Handlebars skills to be done . Typically a super user or a webmaster could do that. 'Advanced' customizations : these include major updates like adding a new data source, layout, component or suggestions provider. These are build from scratch and require SharePoint Framework development skills to be done . Typically, a front-end/SharePoint developer could do that. Note Extensibility samples are centralized in a dedicated repository: https://github.com/microsoft-search/pnp-modern-search-extensibility-samples/tree/main Basic customizations \u00b6 'Basic' customizations cover the layout templates updates with HTML, CSS and Handlebars. Refer to the templating documentation to know more. Advanced customizations \u00b6 The solution uses the concept of 'extensibility libraries' . Basically, these are SharePoint Framework library components you put in the global or site collection app catalog that will be loaded automatically by Web Parts to enhance the experience and options (ex: new data source with new options, custom layout, etc.). Simple as that! As a demonstration of capabilities, all builtin data sources, layouts, web components or suggestions providers are built using the same exact interfaces and methods that are publicly available in the @pnp/modern-search-extensibility SPFx library project. All documentation procedures for extensions are based on the demo extensibility library available in the same repository that you can use as reference. Prerequistes \u00b6 For your project to be a valid extensibility library, you must have the following prerequisites: Your project must be an SPFx library component . The main entry point of your library must implement the IExtensibilityLibrary interface from the @pnp/modern-search-extensibility library. You library manifest ID must be registered in the Web Part where you want to use the extension. SPFx version The SPFx library project must use the same SPFx version as the main solution (currently 1.15.2 ). Owherwise you may face issues at build time. See GitHub issue #1893 Supported extensions \u00b6 Each Web Part type in the solution supports several extensions or no extension at all. It means even your extensibility library contains all possible extensions, they won't be loaded if the Web Part does not support them. Web Part type Supported extensions Search Results Custom web components. Custom Handlebars customizations (ex: helpers, partials ,etc.). Custom event handlers for adaptive cards actions Custom Data Sources Custom query modifier Search Filters Custom web components ( not directly but via the 'Search Results' Web Part extensibility library registration ). Search box Custom suggestions providers. Search Verticals None. Register your extensibility library with a Web Part \u00b6 When a Web Part type supports one or multiple extensions, you can register them going to the last property pane confguration page in the 'Extensibility configuration' section: From here, you can add the manifest IDs of your libraries and decide to enable or disabled certain libraries. The manifest ID can be found in the .manifest.json file: Multiple librairies can be registred for a single Web Part instance allowing you to split your extensions into multiple projects (in the end, they will be all concatenated). For instance, this could be convenient when extensions come from different IT providers. Create an extensibility library \u00b6 To create an extensibility library, you have the choice to reuse the one provided in the GitHub repository or start from scratch. In this case: Create a new SharePoint Framework project of type 'Library' with yo @microsoft/sharepoint . Add an npm reference to @pnp/modern-search-extensibility library using npm i @pnp/modern-search-extensibility --save cmd. In the main entry point, implement the IExtensibilityLibrary interface. Provide all method implementations (return empty arrays if you don't implement specific extensions). Implement your extension(s) depending of the type: Layout Web component Suggestions providers Handlebars customizations Adaptive Cards Actions handlers Query modifier Data Sources Creation process always follows more or less the same pattern: Create the extension data logic or render logic. Register the information about the extension to be discovered and instanciated by the target Web Part by implementing the corresponding method according to the IExtensibilityLibrary interface. Bundle gulp bundle --ship and package gulp package-solution --ship and add the solution to the global or site collection catalog (for this one, it must be the same site collection where the Web Part loading that extension(s) is present). Register your manifest ID in the target Web Part instance . Enjoy! Debug a library component \u00b6 Debugging a library component is exactly the same as debugging an SPFx Web Part. Run gulp serve in the hosted workbench and put a 'Search Results' , 'Search Filters' or 'Search Box' Web Part depending the extension you want to test. If registered correctly, your breakpoints will be triggerred by the main Web Part loading your extension. Accessing the SharePoint Framework context and services in a library component \u00b6 In case you need to access the SharePoint Framework context and services, within your custom library component, you can easily do that by relying on the Service Locator pattern available in SPFx. You simply need to declare a public static property with name serviceKey in your library component and provide a constructor that accepts a ServiceScope instance as input argument. For example, here you can see a code excerpt of such a library component that handles custom actions for Adaptive Cards rendering: import { IAdaptiveCardAction , IComponentDefinition , IExtensibilityLibrary , ILayoutDefinition , ISuggestionProviderDefinition , IQueryModifierDefinition } from '@pnp/modern-search-extensibility' ; import { ServiceKey , ServiceScope } from '@microsoft/sp-core-library' ; import { SPHttpClient , SPHttpClientResponse } from '@microsoft/sp-http' ; import { PageContext } from '@microsoft/sp-page-context' ; export class MyCustomLibraryComponent implements IExtensibilityLibrary { public static readonly serviceKey : ServiceKey < MyCustomLibraryComponent > = ServiceKey . create < MyCustomLibraryComponent > ( 'SPFx:MyCustomLibraryComponent' , MyCustomLibraryComponent ); private _spHttpClient : SPHttpClient ; private _pageContext : PageContext ; private _currentWebUrl : string ; constructor ( serviceScope : ServiceScope ) { serviceScope . whenFinished (() => { this . _spHttpClient = serviceScope . consume ( SPHttpClient . serviceKey ); this . _pageContext = serviceScope . consume ( PageContext . serviceKey ); this . _currentWebUrl = this . _pageContext . web . absoluteUrl ; }); } public getCustomLayouts () : ILayoutDefinition [] { return []; } public getCustomWebComponents () : IComponentDefinition < any > [] { return []; } public getCustomSuggestionProviders () : ISuggestionProviderDefinition [] { return []; } public registerHandlebarsCustomizations ? ( handlebarsNamespace : typeof Handlebars ) : void { } public getCustomQueryModifiers ? () : IQueryModifierDefinition []{ } public invokeCardAction ( action : IAdaptiveCardAction ) : void { // Process the action based on type if ( action . type == \"Action.OpenUrl\" ) { window . open ( action . url , \"_blank\" ); } else if ( action . type == \"Action.Submit\" ) { // Process the Submit action based on title switch ( action . title . toLowerCase ()) { case \"user\" : // Invoke the currentUser endpoint this . _spHttpClient . get ( ` ${ this . _currentWebUrl } /_api/web/currentUser` , SPHttpClient . configurations . v1 , null ). then (( response : SPHttpClientResponse ) => { return response . json (); }); break ; default : console.log ( 'Action not supported!' ); break ; } } } public getCustomDataSources () : IDataSourceDefinition [] { return [ { name : 'Custom Data Source' , iconName : 'Database' , key : 'CustomDataSource' , serviceKey : ServiceKey.create < IDataSource > ( 'CustomDataSource' , CustomDataSource ) } ]; } public name () : string { return 'MyCustomLibraryComponent' ; } } In order to run the above sample code, you will need to import in your library the following npm packages: @microsoft/sp-component-base , @microsoft/sp-core-library , and @microsoft/sp-webpart-base .","title":"Introduction"},{"location":"extensibility/#extensibility-possibilities","text":"This solution supports different levels of customizations depending your requirements: 'Basic' customizations : these include custom settings for data sources, search box, verticals and filters Web Parts + minor updates to existing layouts by adding custom HTML markup (ex: add a custom field in the UI from a data source), updates to builtin layouts fields ('Cards','Details List' and 'People'), etc. They only require HTML, CSS and Handlebars skills to be done . Typically a super user or a webmaster could do that. 'Advanced' customizations : these include major updates like adding a new data source, layout, component or suggestions provider. These are build from scratch and require SharePoint Framework development skills to be done . Typically, a front-end/SharePoint developer could do that. Note Extensibility samples are centralized in a dedicated repository: https://github.com/microsoft-search/pnp-modern-search-extensibility-samples/tree/main","title":"Extensibility possibilities"},{"location":"extensibility/#basic-customizations","text":"'Basic' customizations cover the layout templates updates with HTML, CSS and Handlebars. Refer to the templating documentation to know more.","title":"Basic customizations"},{"location":"extensibility/#advanced-customizations","text":"The solution uses the concept of 'extensibility libraries' . Basically, these are SharePoint Framework library components you put in the global or site collection app catalog that will be loaded automatically by Web Parts to enhance the experience and options (ex: new data source with new options, custom layout, etc.). Simple as that! As a demonstration of capabilities, all builtin data sources, layouts, web components or suggestions providers are built using the same exact interfaces and methods that are publicly available in the @pnp/modern-search-extensibility SPFx library project. All documentation procedures for extensions are based on the demo extensibility library available in the same repository that you can use as reference.","title":"Advanced customizations"},{"location":"extensibility/#prerequistes","text":"For your project to be a valid extensibility library, you must have the following prerequisites: Your project must be an SPFx library component . The main entry point of your library must implement the IExtensibilityLibrary interface from the @pnp/modern-search-extensibility library. You library manifest ID must be registered in the Web Part where you want to use the extension. SPFx version The SPFx library project must use the same SPFx version as the main solution (currently 1.15.2 ). Owherwise you may face issues at build time. See GitHub issue #1893","title":"Prerequistes"},{"location":"extensibility/#supported-extensions","text":"Each Web Part type in the solution supports several extensions or no extension at all. It means even your extensibility library contains all possible extensions, they won't be loaded if the Web Part does not support them. Web Part type Supported extensions Search Results Custom web components. Custom Handlebars customizations (ex: helpers, partials ,etc.). Custom event handlers for adaptive cards actions Custom Data Sources Custom query modifier Search Filters Custom web components ( not directly but via the 'Search Results' Web Part extensibility library registration ). Search box Custom suggestions providers. Search Verticals None.","title":"Supported extensions"},{"location":"extensibility/#register-your-extensibility-library-with-a-web-part","text":"When a Web Part type supports one or multiple extensions, you can register them going to the last property pane confguration page in the 'Extensibility configuration' section: From here, you can add the manifest IDs of your libraries and decide to enable or disabled certain libraries. The manifest ID can be found in the .manifest.json file: Multiple librairies can be registred for a single Web Part instance allowing you to split your extensions into multiple projects (in the end, they will be all concatenated). For instance, this could be convenient when extensions come from different IT providers.","title":"Register your extensibility library with a Web Part"},{"location":"extensibility/#create-an-extensibility-library","text":"To create an extensibility library, you have the choice to reuse the one provided in the GitHub repository or start from scratch. In this case: Create a new SharePoint Framework project of type 'Library' with yo @microsoft/sharepoint . Add an npm reference to @pnp/modern-search-extensibility library using npm i @pnp/modern-search-extensibility --save cmd. In the main entry point, implement the IExtensibilityLibrary interface. Provide all method implementations (return empty arrays if you don't implement specific extensions). Implement your extension(s) depending of the type: Layout Web component Suggestions providers Handlebars customizations Adaptive Cards Actions handlers Query modifier Data Sources Creation process always follows more or less the same pattern: Create the extension data logic or render logic. Register the information about the extension to be discovered and instanciated by the target Web Part by implementing the corresponding method according to the IExtensibilityLibrary interface. Bundle gulp bundle --ship and package gulp package-solution --ship and add the solution to the global or site collection catalog (for this one, it must be the same site collection where the Web Part loading that extension(s) is present). Register your manifest ID in the target Web Part instance . Enjoy!","title":"Create an extensibility library"},{"location":"extensibility/#debug-a-library-component","text":"Debugging a library component is exactly the same as debugging an SPFx Web Part. Run gulp serve in the hosted workbench and put a 'Search Results' , 'Search Filters' or 'Search Box' Web Part depending the extension you want to test. If registered correctly, your breakpoints will be triggerred by the main Web Part loading your extension.","title":"Debug a library component"},{"location":"extensibility/#accessing-the-sharepoint-framework-context-and-services-in-a-library-component","text":"In case you need to access the SharePoint Framework context and services, within your custom library component, you can easily do that by relying on the Service Locator pattern available in SPFx. You simply need to declare a public static property with name serviceKey in your library component and provide a constructor that accepts a ServiceScope instance as input argument. For example, here you can see a code excerpt of such a library component that handles custom actions for Adaptive Cards rendering: import { IAdaptiveCardAction , IComponentDefinition , IExtensibilityLibrary , ILayoutDefinition , ISuggestionProviderDefinition , IQueryModifierDefinition } from '@pnp/modern-search-extensibility' ; import { ServiceKey , ServiceScope } from '@microsoft/sp-core-library' ; import { SPHttpClient , SPHttpClientResponse } from '@microsoft/sp-http' ; import { PageContext } from '@microsoft/sp-page-context' ; export class MyCustomLibraryComponent implements IExtensibilityLibrary { public static readonly serviceKey : ServiceKey < MyCustomLibraryComponent > = ServiceKey . create < MyCustomLibraryComponent > ( 'SPFx:MyCustomLibraryComponent' , MyCustomLibraryComponent ); private _spHttpClient : SPHttpClient ; private _pageContext : PageContext ; private _currentWebUrl : string ; constructor ( serviceScope : ServiceScope ) { serviceScope . whenFinished (() => { this . _spHttpClient = serviceScope . consume ( SPHttpClient . serviceKey ); this . _pageContext = serviceScope . consume ( PageContext . serviceKey ); this . _currentWebUrl = this . _pageContext . web . absoluteUrl ; }); } public getCustomLayouts () : ILayoutDefinition [] { return []; } public getCustomWebComponents () : IComponentDefinition < any > [] { return []; } public getCustomSuggestionProviders () : ISuggestionProviderDefinition [] { return []; } public registerHandlebarsCustomizations ? ( handlebarsNamespace : typeof Handlebars ) : void { } public getCustomQueryModifiers ? () : IQueryModifierDefinition []{ } public invokeCardAction ( action : IAdaptiveCardAction ) : void { // Process the action based on type if ( action . type == \"Action.OpenUrl\" ) { window . open ( action . url , \"_blank\" ); } else if ( action . type == \"Action.Submit\" ) { // Process the Submit action based on title switch ( action . title . toLowerCase ()) { case \"user\" : // Invoke the currentUser endpoint this . _spHttpClient . get ( ` ${ this . _currentWebUrl } /_api/web/currentUser` , SPHttpClient . configurations . v1 , null ). then (( response : SPHttpClientResponse ) => { return response . json (); }); break ; default : console.log ( 'Action not supported!' ); break ; } } } public getCustomDataSources () : IDataSourceDefinition [] { return [ { name : 'Custom Data Source' , iconName : 'Database' , key : 'CustomDataSource' , serviceKey : ServiceKey.create < IDataSource > ( 'CustomDataSource' , CustomDataSource ) } ]; } public name () : string { return 'MyCustomLibraryComponent' ; } } In order to run the above sample code, you will need to import in your library the following npm packages: @microsoft/sp-component-base , @microsoft/sp-core-library , and @microsoft/sp-webpart-base .","title":"Accessing the SharePoint Framework context and services in a library component"},{"location":"extensibility/adaptivecards_customizations/","text":"Register Adaptive Cards Actions handlers customizations \u00b6 If you want to render the search results using a custom Adaptive Card, you might also want to handle custom events upon actions happening in the Adaptive Cards instances. To register a new Adaptive Cards Actions handler customization for the targeted Web Part (i.e. the Web Part instances where the extensibility library is registered and enabled): In the library main entry point (i.e. the class implementing the IExtensibilityLibrary in interface), implement the invokeCardAction(action: any): void method. From within the method write your own implementation of any of the custom actions that you want to handle. public invokeCardAction ( action : any ) : void { // Process the action based on type if ( action . type == \"Action.OpenUrl\" ) { window . open ( action . url , \"_blank\" ); } else if ( action . type == \"Action.Submit\" ) { // Process the action based on title switch ( action . title ) { case 'Click on item' : console . log ( action . data ); break ; case 'Global click' : alert ( action ); break ; default : console.log ( 'Action not supported!' ); break ; } } } In the JSON of the custom Adaptive Card, you can define the custom actions, like in the following code excerpt: { \"$schema\" : \"http://adaptivecards.io/schemas/adaptive-card.json\" , \"type\" : \"AdaptiveCard\" , \"version\" : \"1.3\" , \"body\" : [ { \"type\" : \"TextBlock\" , \"text\" : \"**${$root.data.totalItemsCount}** results\" , \"size\" : \"Medium\" , \"wrap\" : true , \"$when\" : \"${$root.properties.showResultsCount == true}\" }, { \"type\" : \"Container\" , \"$data\" : \"${data.items}\" , \"items\" : [ { \"type\" : \"ColumnSet\" , \"id\" : \"${hitId}\" , \"columns\" : [ { \"type\" : \"Column\" , \"items\" : [ { \"type\" : \"TextBlock\" , \"wrap\" : true , \"text\" : \"\" } ], \"width\" : \"auto\" }, { \"type\" : \"Column\" , \"items\" : [ { \"type\" : \"TextBlock\" , \"wrap\" : true , \"text\" : \"[${string(jPath($data, concat('.',$root.slots['Title']))[0])}](${string(jPath($data, concat('.',$root.slots['Path']))[0])})\" } ], \"width\" : \"auto\" }, { \"type\" : \"Column\" , \"items\" : [ { \"type\" : \"ActionSet\" , \"actions\" : [ { \"type\" : \"Action.Submit\" , \"title\" : \"Click on item\" , \"style\" : \"positive\" , \"data\" : { \"id\" : \"123\" } } ], \"spacing\" : \"medium\" } ], \"width\" : \"auto\" } ] } ] } ], \"actions\" : [ { \"type\" : \"Action.Submit\" , \"title\" : \"Global click\" , \"data\" : { \"id\" : \"456\" } }, { \"type\" : \"Action.OpenUrl\" , \"title\" : \"Open URL\" , \"url\" : \"https://pnp.github.io/\" } ] }","title":"Custom event handlers for adaptive cards actions"},{"location":"extensibility/adaptivecards_customizations/#register-adaptive-cards-actions-handlers-customizations","text":"If you want to render the search results using a custom Adaptive Card, you might also want to handle custom events upon actions happening in the Adaptive Cards instances. To register a new Adaptive Cards Actions handler customization for the targeted Web Part (i.e. the Web Part instances where the extensibility library is registered and enabled): In the library main entry point (i.e. the class implementing the IExtensibilityLibrary in interface), implement the invokeCardAction(action: any): void method. From within the method write your own implementation of any of the custom actions that you want to handle. public invokeCardAction ( action : any ) : void { // Process the action based on type if ( action . type == \"Action.OpenUrl\" ) { window . open ( action . url , \"_blank\" ); } else if ( action . type == \"Action.Submit\" ) { // Process the action based on title switch ( action . title ) { case 'Click on item' : console . log ( action . data ); break ; case 'Global click' : alert ( action ); break ; default : console.log ( 'Action not supported!' ); break ; } } } In the JSON of the custom Adaptive Card, you can define the custom actions, like in the following code excerpt: { \"$schema\" : \"http://adaptivecards.io/schemas/adaptive-card.json\" , \"type\" : \"AdaptiveCard\" , \"version\" : \"1.3\" , \"body\" : [ { \"type\" : \"TextBlock\" , \"text\" : \"**${$root.data.totalItemsCount}** results\" , \"size\" : \"Medium\" , \"wrap\" : true , \"$when\" : \"${$root.properties.showResultsCount == true}\" }, { \"type\" : \"Container\" , \"$data\" : \"${data.items}\" , \"items\" : [ { \"type\" : \"ColumnSet\" , \"id\" : \"${hitId}\" , \"columns\" : [ { \"type\" : \"Column\" , \"items\" : [ { \"type\" : \"TextBlock\" , \"wrap\" : true , \"text\" : \"\" } ], \"width\" : \"auto\" }, { \"type\" : \"Column\" , \"items\" : [ { \"type\" : \"TextBlock\" , \"wrap\" : true , \"text\" : \"[${string(jPath($data, concat('.',$root.slots['Title']))[0])}](${string(jPath($data, concat('.',$root.slots['Path']))[0])})\" } ], \"width\" : \"auto\" }, { \"type\" : \"Column\" , \"items\" : [ { \"type\" : \"ActionSet\" , \"actions\" : [ { \"type\" : \"Action.Submit\" , \"title\" : \"Click on item\" , \"style\" : \"positive\" , \"data\" : { \"id\" : \"123\" } } ], \"spacing\" : \"medium\" } ], \"width\" : \"auto\" } ] } ] } ], \"actions\" : [ { \"type\" : \"Action.Submit\" , \"title\" : \"Global click\" , \"data\" : { \"id\" : \"456\" } }, { \"type\" : \"Action.OpenUrl\" , \"title\" : \"Open URL\" , \"url\" : \"https://pnp.github.io/\" } ] }","title":"Register Adaptive Cards Actions handlers customizations"},{"location":"extensibility/custom_data_sources/","text":"Create a custom data source \u00b6 Custom data sources can be added to a search results Web Part to get results from your custom source. Custom data source creation process \u00b6 Custom data source creation process comes in two distinct steps: Create the data source logic . Register the data source information for discovery . Create the data source logic \u00b6 In your extensibility library project, create a new CustomDataSource.ts TypeScript file. Create an interface for your data source properties, typically the ones you want to persist in the Web Part property bag. Data source properties are isolated from the other general Web Part properties under the property dataSourceProperties in the property bag object. export interface ICustomDataSourceProperties { possibleResults : string ; } Implement the BaseDataSource abstract class using your properties interface: export class CustomDataSource extends BaseDataSource < ICustomDataSourceProperties > { ... } Implement your data source logic according to the available methods and properties. BaseDataSource - Methods \u00b6 Method Description onInit() The initialization method of your data source (ex: initialize your properties, etc.). You can perform asynchronous calls here. This method will be called when the data source is instantiated by the main Web Part. getPropertyPaneGroupsConfiguration() Returns the property pane fields to display when your data source is selected. These are regular SPFx property fields and groups. Data source properties are isolated from the other general Web Part properties under the property dataSourceProperties . It means you must include that path in your property pane controls to get the value persisted. Defining fields or groups is not mandatory for a provider. If you don't want to expose any option, just return an empty array. onPropertyUpdate() The method will be called when a property pane value is updated. The main Web Part in Reactive mode for property pane fields. getPagingBehavior() The method should return the desired paging behavior for the data source. Will be 'None' if not specified. getFilterBehavior() The method should return the desired filter behavior for the data source. Will be 'Static' if not specified. getAppliedFilters() If any, this method should return the list of filters (i.e data source fields) applied by the data source to filter results. getItemCount() The method should return the total number of items. This information will be used to generate page numbers. getTemplateSlots() The method should return the available template slots for this data source. getSortableFields() The method should return the list of sortable fields for the data source if applicable BaseDataSource - Properties \u00b6 Property Description properties The Web Part properties in the property bag. Corresponds to the isolated dataSourceProperties property in the global property bag. You won't be able to access any other general properties of the Web Part. Register provider information \u00b6 The next step is to provide information about your new data source. In the library main entry point (i.e. the class implementing the IExtensibilityLibrary in interface) return a new IDataSourceDefinition array in the getCustomDataSources() method using these properties: Property Description name The friendly name of your data source that will show up in the configuration panel. iconName The name of an icon from Office UI Fabric/Fluent UI that will be shown in the data source options. key An unique internal key for your data source. serviceKey A service key used to instantiate your data source class. Builtin or custom data sources are instantiated dynamically using SPFx service scopes . public getCustomDataSources () : IDataSourceDefinition [] { return [ { name : 'Custom Data Source' , iconName : 'Database' , key : 'CustomDataSource' , serviceKey : ServiceKey.create < IDataSource > ( 'CustomDataSource' , CustomDataSource ) } ]; }","title":"Custom data sources"},{"location":"extensibility/custom_data_sources/#create-a-custom-data-source","text":"Custom data sources can be added to a search results Web Part to get results from your custom source.","title":"Create a custom data source"},{"location":"extensibility/custom_data_sources/#custom-data-source-creation-process","text":"Custom data source creation process comes in two distinct steps: Create the data source logic . Register the data source information for discovery .","title":"Custom data source creation process"},{"location":"extensibility/custom_data_sources/#create-the-data-source-logic","text":"In your extensibility library project, create a new CustomDataSource.ts TypeScript file. Create an interface for your data source properties, typically the ones you want to persist in the Web Part property bag. Data source properties are isolated from the other general Web Part properties under the property dataSourceProperties in the property bag object. export interface ICustomDataSourceProperties { possibleResults : string ; } Implement the BaseDataSource abstract class using your properties interface: export class CustomDataSource extends BaseDataSource < ICustomDataSourceProperties > { ... } Implement your data source logic according to the available methods and properties.","title":"Create the data source logic"},{"location":"extensibility/custom_data_sources/#basedatasource-methods","text":"Method Description onInit() The initialization method of your data source (ex: initialize your properties, etc.). You can perform asynchronous calls here. This method will be called when the data source is instantiated by the main Web Part. getPropertyPaneGroupsConfiguration() Returns the property pane fields to display when your data source is selected. These are regular SPFx property fields and groups. Data source properties are isolated from the other general Web Part properties under the property dataSourceProperties . It means you must include that path in your property pane controls to get the value persisted. Defining fields or groups is not mandatory for a provider. If you don't want to expose any option, just return an empty array. onPropertyUpdate() The method will be called when a property pane value is updated. The main Web Part in Reactive mode for property pane fields. getPagingBehavior() The method should return the desired paging behavior for the data source. Will be 'None' if not specified. getFilterBehavior() The method should return the desired filter behavior for the data source. Will be 'Static' if not specified. getAppliedFilters() If any, this method should return the list of filters (i.e data source fields) applied by the data source to filter results. getItemCount() The method should return the total number of items. This information will be used to generate page numbers. getTemplateSlots() The method should return the available template slots for this data source. getSortableFields() The method should return the list of sortable fields for the data source if applicable","title":"BaseDataSource - Methods"},{"location":"extensibility/custom_data_sources/#basedatasource-properties","text":"Property Description properties The Web Part properties in the property bag. Corresponds to the isolated dataSourceProperties property in the global property bag. You won't be able to access any other general properties of the Web Part.","title":"BaseDataSource - Properties"},{"location":"extensibility/custom_data_sources/#register-provider-information","text":"The next step is to provide information about your new data source. In the library main entry point (i.e. the class implementing the IExtensibilityLibrary in interface) return a new IDataSourceDefinition array in the getCustomDataSources() method using these properties: Property Description name The friendly name of your data source that will show up in the configuration panel. iconName The name of an icon from Office UI Fabric/Fluent UI that will be shown in the data source options. key An unique internal key for your data source. serviceKey A service key used to instantiate your data source class. Builtin or custom data sources are instantiated dynamically using SPFx service scopes . public getCustomDataSources () : IDataSourceDefinition [] { return [ { name : 'Custom Data Source' , iconName : 'Database' , key : 'CustomDataSource' , serviceKey : ServiceKey.create < IDataSource > ( 'CustomDataSource' , CustomDataSource ) } ]; }","title":"Register provider information"},{"location":"extensibility/custom_layout/","text":"Create a custom layout \u00b6 Custom layouts are only supported for the 'Search Results' Web Part. You can't add custom layout for the 'Search Filters' Web Part. Layout creation process \u00b6 Same as data source, the layout creation process comes in three distinct steps: Create the layout class (i.e. define the property pane options) . Create the HTML template associated to that layout . Register the layout information for discovery . Create the layout \u00b6 In your extensibility library project, create a new MyLayout.ts TypeScript file. Create an interface for your layout properties, typically the ones you want to persist in the Web Part property bag. Layout properties are isolated from the other general Web Part properties under the property layoutProperties in the property bag object. export interface ICustomLayoutProperties { myTextProperty : string ; } Implement the BaseLayout abstract class using your properties interface: export class Customlayout extends BaseLayout < ICustomLayoutProperties > { ... } Implement your layout logic according to the available methods and properties. BaseLayout - Methods \u00b6 Method Description onInit() The initialization method of your layout (ex: initialize your properties, etc.). You can perform asynchronous calls here. This method will be called when the layout is instanciated by the main Web Part. getPropertyPaneGroupsConfiguration() Returns the property pane fields to display when your layout is selected. These are regular SPFx property fields and groups. Layout properties are isolated from the other general Web Part properties under the property layoutProperties . It means you must include that path in your property pane controls get the value persisted (same thing as custom data source). Defining fields or groups is not mandatory for a layout. If you don't want to expose any option, just return an empty array. onPropertyUpdate() The method will be called when a property pane value is updated. The main Web Part in Reactive mode for property pane fields. BaseLayout - Properties \u00b6 Property Description properties The Web Part properties in the property bag. Corresponds to the isolated layoutProperties property in the global property bag. You won't be able to access any other general properties of the Web Part. Create the HTML template file (Handlebars) \u00b6 In your extensibility library project, create a new custom-layout.html HTML file. A layout template is split into two distinct parts: A template part, containing the HTML markup to display your data once fetched . This part is mandatory to display your data. < content id = \"template\" > A placeholder part, containing the HTML markup to display as placeholder while the data are getting fetched . This part is optional. < content id = \"placeholder\" > In a template, you must use Handlebars expressions to access and display your data. Example: iterating through all items {{ #each data.items as | item | }} {{ /each }} Register layout information \u00b6 The next step is to fill information about your new layout. In the library main entry point (i.e. the class implementing the IExtensibilityLibrary in interface) return a new ILayoutDefinition object in the getCustomLayouts() method using these properties: Property Description name The friendly name of your layout that will show up in tiles. iconName An Office UI Fabric icon for your layout. key An unique internal key for your layout. type The layout type ( LayoutType.Results is for the 'Data Visualizer' Web Part, LayoutType.Filter for the 'Data Filter' Web Part). Only LayoutType.Results is supported for now. You can't add custom layout for the 'Data Filter' Web Part. templateContent The template HTML content as string. Use a require statement to get the string content from your HTML file. If you reference a JSON file, you must use the stringified value (ex: JSON.stringify(require('../custom-layout.json'), null, \"\\t\") ) serviceKey A service key used to instanciate your layout class. Builtin or custom data layouts are instanciated dynamically using SPFx service scopes . public getCustomLayouts () : ILayoutDefinition [] { return [ { name : 'My custom layout' , iconName : 'Color' , key : 'CustomLayout' , type : LayoutType . Results , templateContent : require ( '../custom-layout.html' ), serviceKey : ServiceKey.create < ILayout > ( 'MyCompany:CustomLayout' , Customlayout ) } ]; }","title":"Custom layout"},{"location":"extensibility/custom_layout/#create-a-custom-layout","text":"Custom layouts are only supported for the 'Search Results' Web Part. You can't add custom layout for the 'Search Filters' Web Part.","title":"Create a custom layout"},{"location":"extensibility/custom_layout/#layout-creation-process","text":"Same as data source, the layout creation process comes in three distinct steps: Create the layout class (i.e. define the property pane options) . Create the HTML template associated to that layout . Register the layout information for discovery .","title":"Layout creation process"},{"location":"extensibility/custom_layout/#create-the-layout","text":"In your extensibility library project, create a new MyLayout.ts TypeScript file. Create an interface for your layout properties, typically the ones you want to persist in the Web Part property bag. Layout properties are isolated from the other general Web Part properties under the property layoutProperties in the property bag object. export interface ICustomLayoutProperties { myTextProperty : string ; } Implement the BaseLayout abstract class using your properties interface: export class Customlayout extends BaseLayout < ICustomLayoutProperties > { ... } Implement your layout logic according to the available methods and properties.","title":"Create the layout"},{"location":"extensibility/custom_layout/#baselayout-methods","text":"Method Description onInit() The initialization method of your layout (ex: initialize your properties, etc.). You can perform asynchronous calls here. This method will be called when the layout is instanciated by the main Web Part. getPropertyPaneGroupsConfiguration() Returns the property pane fields to display when your layout is selected. These are regular SPFx property fields and groups. Layout properties are isolated from the other general Web Part properties under the property layoutProperties . It means you must include that path in your property pane controls get the value persisted (same thing as custom data source). Defining fields or groups is not mandatory for a layout. If you don't want to expose any option, just return an empty array. onPropertyUpdate() The method will be called when a property pane value is updated. The main Web Part in Reactive mode for property pane fields.","title":"BaseLayout - Methods"},{"location":"extensibility/custom_layout/#baselayout-properties","text":"Property Description properties The Web Part properties in the property bag. Corresponds to the isolated layoutProperties property in the global property bag. You won't be able to access any other general properties of the Web Part.","title":"BaseLayout - Properties"},{"location":"extensibility/custom_layout/#create-the-html-template-file-handlebars","text":"In your extensibility library project, create a new custom-layout.html HTML file. A layout template is split into two distinct parts: A template part, containing the HTML markup to display your data once fetched . This part is mandatory to display your data. < content id = \"template\" > A placeholder part, containing the HTML markup to display as placeholder while the data are getting fetched . This part is optional. < content id = \"placeholder\" > In a template, you must use Handlebars expressions to access and display your data. Example: iterating through all items {{ #each data.items as | item | }} {{ /each }}","title":"Create the HTML template file (Handlebars)"},{"location":"extensibility/custom_layout/#register-layout-information","text":"The next step is to fill information about your new layout. In the library main entry point (i.e. the class implementing the IExtensibilityLibrary in interface) return a new ILayoutDefinition object in the getCustomLayouts() method using these properties: Property Description name The friendly name of your layout that will show up in tiles. iconName An Office UI Fabric icon for your layout. key An unique internal key for your layout. type The layout type ( LayoutType.Results is for the 'Data Visualizer' Web Part, LayoutType.Filter for the 'Data Filter' Web Part). Only LayoutType.Results is supported for now. You can't add custom layout for the 'Data Filter' Web Part. templateContent The template HTML content as string. Use a require statement to get the string content from your HTML file. If you reference a JSON file, you must use the stringified value (ex: JSON.stringify(require('../custom-layout.json'), null, \"\\t\") ) serviceKey A service key used to instanciate your layout class. Builtin or custom data layouts are instanciated dynamically using SPFx service scopes . public getCustomLayouts () : ILayoutDefinition [] { return [ { name : 'My custom layout' , iconName : 'Color' , key : 'CustomLayout' , type : LayoutType . Results , templateContent : require ( '../custom-layout.html' ), serviceKey : ServiceKey.create < ILayout > ( 'MyCompany:CustomLayout' , Customlayout ) } ]; }","title":"Register layout information"},{"location":"extensibility/custom_query_modifications/","text":"Create a custom query modifier \u00b6 Custom query modifier can be added to a search result Web Part to modify search requests before they are sent to the server. A query modifier supports: Modification of query text : a query modifier can alter the query text. Sorted modifications : modifier can be sorted and are executed in order - but you can set a modifier to stop further modifications. Custom modifier creation process \u00b6 Custom modifier creation process comes in two distinct steps: Create the modifier logic . Register the modifier information for discovery . Create the provider logic \u00b6 In your extensibility library project, create a new CustomQueryModifier.ts TypeScript file. Create an interface for your modifier properties, typically the ones you want to persist in the Web Part property bag. Modifier properties are isolated from the other general Web Part properties under the property queryModifierProperties in the property bag object. export interface ICustomQueryModifierProperties { myProperty : string ; } Implement the BaseQueryModifier abstract class using your properties interface: export class CustomQueryModifier extends BaseQueryModifier < ICustomQueryModifierProperties > { ... } Implement your query modifier logic according to the available methods and properties. BaseQueryModifier - Methods \u00b6 Method Description onInit() The initialization method of your query modifier (ex: initialize your properties, etc.). You can perform asynchronous calls here. This method will be called when the provider is instanciated by the main Web Part. This is a good place to initialize any consumed services if any. modifyQuery() Method called to get a query modification when a search is requested. getPropertyPaneGroupsConfiguration() Returns the property pane fields to display when your query modifier is selected. These are regular SPFx property fields and groups. Query modifier properties are isolated from the other general Web Part properties under the property queryModifierProperties . It means you must include that path in your property pane controls get the value persisted. Defining fields or groups is not mandatory for a provider. If you don't want to expose any option, just return an empty array. onPropertyUpdate() The method will be called when a property pane value is updated. The main Web Part is in Reactive mode for property pane fields. BaseQueryModifier - Properties \u00b6 Property Description properties The Web Part properties in the property bag. Corresponds to the isolated queryModifierProperties property in the global property bag. You won't be able to access any other general properties of the Web Part. endWhenSuccessfull Flag indicating if this should be the last query modification when the query was modified - can be switched in the query modifier list overview. Register provider information \u00b6 The next step is to fill information about your new query modifier. In the library main entry point (i.e. the class implementing the IExtensibilityLibrary in interface) return a new IQueryModifierDefinition object in the getCustomQueryModifiers() method using these properties: Property Description name The friendly name of your query modifier that will show up in the configuration panel. key An unique internal key for your data source. description A meaningful description of your query modifier. serviceKey A service key used to instanciate your query modifier class. Builtin or custom query modifiers are instanciated dynamically using SPFx service scopes . public getCustomQueryModifiers () : IQueryModifierDefinition [] { return [ { name : 'Custom Query Modifier' , key : 'CustomQueryModifier' , description : 'A demo custom query modifier from the extensibility library' , serviceKey : ServiceKey.create < IQueryModifier > ( 'MyCompany:CustomQueryModifier' , CustomQueryModifier ) } ]; }","title":"Custom query modifier"},{"location":"extensibility/custom_query_modifications/#create-a-custom-query-modifier","text":"Custom query modifier can be added to a search result Web Part to modify search requests before they are sent to the server. A query modifier supports: Modification of query text : a query modifier can alter the query text. Sorted modifications : modifier can be sorted and are executed in order - but you can set a modifier to stop further modifications.","title":"Create a custom query modifier"},{"location":"extensibility/custom_query_modifications/#custom-modifier-creation-process","text":"Custom modifier creation process comes in two distinct steps: Create the modifier logic . Register the modifier information for discovery .","title":"Custom modifier creation process"},{"location":"extensibility/custom_query_modifications/#create-the-provider-logic","text":"In your extensibility library project, create a new CustomQueryModifier.ts TypeScript file. Create an interface for your modifier properties, typically the ones you want to persist in the Web Part property bag. Modifier properties are isolated from the other general Web Part properties under the property queryModifierProperties in the property bag object. export interface ICustomQueryModifierProperties { myProperty : string ; } Implement the BaseQueryModifier abstract class using your properties interface: export class CustomQueryModifier extends BaseQueryModifier < ICustomQueryModifierProperties > { ... } Implement your query modifier logic according to the available methods and properties.","title":"Create the provider logic"},{"location":"extensibility/custom_query_modifications/#basequerymodifier-methods","text":"Method Description onInit() The initialization method of your query modifier (ex: initialize your properties, etc.). You can perform asynchronous calls here. This method will be called when the provider is instanciated by the main Web Part. This is a good place to initialize any consumed services if any. modifyQuery() Method called to get a query modification when a search is requested. getPropertyPaneGroupsConfiguration() Returns the property pane fields to display when your query modifier is selected. These are regular SPFx property fields and groups. Query modifier properties are isolated from the other general Web Part properties under the property queryModifierProperties . It means you must include that path in your property pane controls get the value persisted. Defining fields or groups is not mandatory for a provider. If you don't want to expose any option, just return an empty array. onPropertyUpdate() The method will be called when a property pane value is updated. The main Web Part is in Reactive mode for property pane fields.","title":"BaseQueryModifier - Methods"},{"location":"extensibility/custom_query_modifications/#basequerymodifier-properties","text":"Property Description properties The Web Part properties in the property bag. Corresponds to the isolated queryModifierProperties property in the global property bag. You won't be able to access any other general properties of the Web Part. endWhenSuccessfull Flag indicating if this should be the last query modification when the query was modified - can be switched in the query modifier list overview.","title":"BaseQueryModifier - Properties"},{"location":"extensibility/custom_query_modifications/#register-provider-information","text":"The next step is to fill information about your new query modifier. In the library main entry point (i.e. the class implementing the IExtensibilityLibrary in interface) return a new IQueryModifierDefinition object in the getCustomQueryModifiers() method using these properties: Property Description name The friendly name of your query modifier that will show up in the configuration panel. key An unique internal key for your data source. description A meaningful description of your query modifier. serviceKey A service key used to instanciate your query modifier class. Builtin or custom query modifiers are instanciated dynamically using SPFx service scopes . public getCustomQueryModifiers () : IQueryModifierDefinition [] { return [ { name : 'Custom Query Modifier' , key : 'CustomQueryModifier' , description : 'A demo custom query modifier from the extensibility library' , serviceKey : ServiceKey.create < IQueryModifier > ( 'MyCompany:CustomQueryModifier' , CustomQueryModifier ) } ]; }","title":"Register provider information"},{"location":"extensibility/custom_suggestions_provider/","text":"Create a custom suggestions providers \u00b6 Custom suggestions providers can be added to a search box Web Part to get normalized keywords during search. A suggestions provider supports: Zero term suggestions : suggestions displayed when the search box get the initial focus and no term is provided. Suggestions based on a keywords : suggestions matching specific keywords provided in the search box. Custom suggestions provider creation process \u00b6 Suggestions provider creation process comes in two distinct steps: Create the provider logic . Register the provider information for discovery . Create the provider logic \u00b6 In your extensibility library project, create a new MyProvider.ts TypeScript file. Create an interface for your provider properties, typically the ones you want to persist in the Web Part property bag. Providers properties are isolated from the other general Web Part properties under the property providerProperties in the property bag object. export interface ICustomSuggestionProviderProperties { myProperty : string ; } Implement the BaseSuggestionProvider abstract class using your properties interface: export class CustomSuggestionProvider extends BaseSuggestionProvider < ICustomSuggestionProviderProperties > { ... } Implement your provider logic according to the available methods and properties. BaseSuggestionProvider - Methods \u00b6 Method Description onInit() The initialization method of your provider (ex: initialize your properties, etc.). You can perform asynchronous calls here. This method will be called when the provider is instanciated by the main Web Part. This is a good place to fetch any zero term suggestions if any. getSuggestions() Method called to retrieve suggestions when a keyword is entered (in paramter). getZeroTermSuggestions() Method called to retrieve the zero term suggestions (i.e. when the search box gets initial focus). getPropertyPaneGroupsConfiguration() Returns the property pane fields to display when your provider is selected. These are regular SPFx property fields and groups. PRovider properties are isolated from the other general Web Part properties under the property providerProperties . It means you must include that path in your property pane controls get the value persisted. Defining fields or groups is not mandatory for a provider. If you don't want to expose any option, just return an empty array. onPropertyUpdate() The method will be called when a property pane value is updated. The main Web Part in Reactive mode for property pane fields. BaseSuggestionProvider - Properties \u00b6 Property Description properties The Web Part properties in the property bag. Corresponds to the isolated providerProperties property in the global property bag. You won't be able to access any other general properties of the Web Part. isZeroTermSuggestionsEnabled Flag indicating if the provider supports zero term suggestions or not. Register provider information \u00b6 The next step is to fill information about your new suggestions provider. In the library main entry point (i.e. the class implementing the IExtensibilityLibrary in interface) return a new ISuggestionProviderDefinition object in the getCustomSuggestionProviders() method using these properties: Property Description name The friendly name of your provider that will show up in the configuration panel. key An unique internal key for your data source. description A meaningful description of your provider. serviceKey A service key used to instanciate your provider class. Builtin or custom providers are instanciated dynamically using SPFx service scopes . public getCustomSuggestionProviders () : ISuggestionProviderDefinition [] { return [ { name : 'Custom Suggestions Provider' , key : 'CustomSuggestionsProvider' , description : 'A demo custom suggestions provider from the extensibility library' , serviceKey : ServiceKey.create < ISuggestionProvider > ( 'MyCompany:CustomSuggestionsProvider' , CustomSuggestionProvider ) } ]; }","title":"Custom suggestions provider"},{"location":"extensibility/custom_suggestions_provider/#create-a-custom-suggestions-providers","text":"Custom suggestions providers can be added to a search box Web Part to get normalized keywords during search. A suggestions provider supports: Zero term suggestions : suggestions displayed when the search box get the initial focus and no term is provided. Suggestions based on a keywords : suggestions matching specific keywords provided in the search box.","title":"Create a custom suggestions providers"},{"location":"extensibility/custom_suggestions_provider/#custom-suggestions-provider-creation-process","text":"Suggestions provider creation process comes in two distinct steps: Create the provider logic . Register the provider information for discovery .","title":"Custom suggestions provider creation process"},{"location":"extensibility/custom_suggestions_provider/#create-the-provider-logic","text":"In your extensibility library project, create a new MyProvider.ts TypeScript file. Create an interface for your provider properties, typically the ones you want to persist in the Web Part property bag. Providers properties are isolated from the other general Web Part properties under the property providerProperties in the property bag object. export interface ICustomSuggestionProviderProperties { myProperty : string ; } Implement the BaseSuggestionProvider abstract class using your properties interface: export class CustomSuggestionProvider extends BaseSuggestionProvider < ICustomSuggestionProviderProperties > { ... } Implement your provider logic according to the available methods and properties.","title":"Create the provider logic"},{"location":"extensibility/custom_suggestions_provider/#basesuggestionprovider-methods","text":"Method Description onInit() The initialization method of your provider (ex: initialize your properties, etc.). You can perform asynchronous calls here. This method will be called when the provider is instanciated by the main Web Part. This is a good place to fetch any zero term suggestions if any. getSuggestions() Method called to retrieve suggestions when a keyword is entered (in paramter). getZeroTermSuggestions() Method called to retrieve the zero term suggestions (i.e. when the search box gets initial focus). getPropertyPaneGroupsConfiguration() Returns the property pane fields to display when your provider is selected. These are regular SPFx property fields and groups. PRovider properties are isolated from the other general Web Part properties under the property providerProperties . It means you must include that path in your property pane controls get the value persisted. Defining fields or groups is not mandatory for a provider. If you don't want to expose any option, just return an empty array. onPropertyUpdate() The method will be called when a property pane value is updated. The main Web Part in Reactive mode for property pane fields.","title":"BaseSuggestionProvider - Methods"},{"location":"extensibility/custom_suggestions_provider/#basesuggestionprovider-properties","text":"Property Description properties The Web Part properties in the property bag. Corresponds to the isolated providerProperties property in the global property bag. You won't be able to access any other general properties of the Web Part. isZeroTermSuggestionsEnabled Flag indicating if the provider supports zero term suggestions or not.","title":"BaseSuggestionProvider - Properties"},{"location":"extensibility/custom_suggestions_provider/#register-provider-information","text":"The next step is to fill information about your new suggestions provider. In the library main entry point (i.e. the class implementing the IExtensibilityLibrary in interface) return a new ISuggestionProviderDefinition object in the getCustomSuggestionProviders() method using these properties: Property Description name The friendly name of your provider that will show up in the configuration panel. key An unique internal key for your data source. description A meaningful description of your provider. serviceKey A service key used to instanciate your provider class. Builtin or custom providers are instanciated dynamically using SPFx service scopes . public getCustomSuggestionProviders () : ISuggestionProviderDefinition [] { return [ { name : 'Custom Suggestions Provider' , key : 'CustomSuggestionsProvider' , description : 'A demo custom suggestions provider from the extensibility library' , serviceKey : ServiceKey.create < ISuggestionProvider > ( 'MyCompany:CustomSuggestionsProvider' , CustomSuggestionProvider ) } ]; }","title":"Register provider information"},{"location":"extensibility/custom_web_component/","text":"Create a custom web component \u00b6 What is a web component? \u00b6 A web component is a custom HTML element that can be used in your templates to implement complex behaviors. In the solution we used them here as \"wrappers\" for React components to be able to use them with Handlebars. More information about web components in general can be found here . By default, several components are available ( see the complete list ). If these does not fit your requirement, you can still create your own. Web component creation process \u00b6 Web component creation process comes in two distinct steps: Create the component class and its React sub components . Register the component information for discovery . Create the component logic and sub components \u00b6 A web component is typically composed of these parts: A web component class derived from the native HTMLElement class. A main React component to be rendered inside the web component. To create new component: In your extensibility library project, create a new MyComponent.ts JSX file. Create a new class extending the abstract class BaseWebComponent . This class must have at least the connectedCallback() method from base interface HTMLElement . export class MyCustomComponentWebComponent extends BaseWebComponent { public constructor () { super (); } public async connectedCallback () { ... } } Create a new regular React component (in the same file or a separate file and as class or hook): export interface IObjectParam { myProperty : string ; } export interface ICustomComponentProps { /** * A sample string param */ myStringParam? : string ; /*** * A sample object param */ myObjectParam? : IObjectParam ; } export interface ICustomComponentState { } export class CustomComponent extends React . Component < ICustomComponentProps , ICustomComponentState > { public render () { // Parse custom object const myObject : IObjectParam = this . props . myObjectParam ; return < div > { this . props . myStringParam } { myObject . myProperty } < /div>; } } In this solution, web components are considered stateless , meaning they will be entirely recreated when an attribute is changed (coming from the property pane). It means you can still use an internal state in your React components but not rely on the parent context (props) since it will be recreated every time by the Handlebars template if a property pane value is updated. The componentDidMount() method will be called every time in this case (not componentDidUpdate() ). In your web component class, render your React component: public async connectedCallback () { let props = this . resolveAttributes (); const customComponent = < CustomComponent {... props } /> ; ReactDOM . render ( customComponent , this ); } The resolveAttributes() method will look at all data-* HTML attributes in your web component custom element node and return a corresponding key/value pair object with values in their guessed type that you can pass directly to your React component as props. By convention, web component attributes have to be passed using camel case to be tranformed into React component props. For instance: a data-my-string-param HTML attribute becomes myStringParam prop. Supported guessed types for attributes are boolean , string , date and object . All non supported types will be passed a string . HTML attributes must use the data- prefix to be retrieved correctly. To pass JSON objects, you can use the JSONstringify Handlebars helper. If valid JSON, they will be returned as objects by the resolveAttributes() method. Example < my-custom-component data-my-string-param = \"Default value\" data-my-object-param = \"{{JSONstringify this 2}}\" data-my-date-param = \"01/01/2020\" data-my-boolean-param = \"true\" > Register component information \u00b6 The next step is to fill information about your new component. In the library main entry point (i.e. the class implementing the IExtensibilityLibrary in interface) return a new IComponentDefinition object in the getCustomWebComponents() method using these properties: Property Description componentName The name for your component. This name will be used as the custom HTML element name (ex: ). componentClass The web component class for that component. public getCustomWebComponents () : IComponentDefinition < any > [] { return [ { componentName : 'my-custom-component' , componentClass : MyCustomComponentWebComponent } ]; } Consume services from BaseWebComponent \u00b6 const msGraphClientFactory = this . _serviceScope . consume < MSGraphClientFactory > ( MSGraphClientFactory . serviceKey ); const msGraphClient = await msGraphClientFactory . getClient ();","title":"Custom web component"},{"location":"extensibility/custom_web_component/#create-a-custom-web-component","text":"","title":"Create a custom web component"},{"location":"extensibility/custom_web_component/#what-is-a-web-component","text":"A web component is a custom HTML element that can be used in your templates to implement complex behaviors. In the solution we used them here as \"wrappers\" for React components to be able to use them with Handlebars. More information about web components in general can be found here . By default, several components are available ( see the complete list ). If these does not fit your requirement, you can still create your own.","title":"What is a web component?"},{"location":"extensibility/custom_web_component/#web-component-creation-process","text":"Web component creation process comes in two distinct steps: Create the component class and its React sub components . Register the component information for discovery .","title":"Web component creation process"},{"location":"extensibility/custom_web_component/#create-the-component-logic-and-sub-components","text":"A web component is typically composed of these parts: A web component class derived from the native HTMLElement class. A main React component to be rendered inside the web component. To create new component: In your extensibility library project, create a new MyComponent.ts JSX file. Create a new class extending the abstract class BaseWebComponent . This class must have at least the connectedCallback() method from base interface HTMLElement . export class MyCustomComponentWebComponent extends BaseWebComponent { public constructor () { super (); } public async connectedCallback () { ... } } Create a new regular React component (in the same file or a separate file and as class or hook): export interface IObjectParam { myProperty : string ; } export interface ICustomComponentProps { /** * A sample string param */ myStringParam? : string ; /*** * A sample object param */ myObjectParam? : IObjectParam ; } export interface ICustomComponentState { } export class CustomComponent extends React . Component < ICustomComponentProps , ICustomComponentState > { public render () { // Parse custom object const myObject : IObjectParam = this . props . myObjectParam ; return < div > { this . props . myStringParam } { myObject . myProperty } < /div>; } } In this solution, web components are considered stateless , meaning they will be entirely recreated when an attribute is changed (coming from the property pane). It means you can still use an internal state in your React components but not rely on the parent context (props) since it will be recreated every time by the Handlebars template if a property pane value is updated. The componentDidMount() method will be called every time in this case (not componentDidUpdate() ). In your web component class, render your React component: public async connectedCallback () { let props = this . resolveAttributes (); const customComponent = < CustomComponent {... props } /> ; ReactDOM . render ( customComponent , this ); } The resolveAttributes() method will look at all data-* HTML attributes in your web component custom element node and return a corresponding key/value pair object with values in their guessed type that you can pass directly to your React component as props. By convention, web component attributes have to be passed using camel case to be tranformed into React component props. For instance: a data-my-string-param HTML attribute becomes myStringParam prop. Supported guessed types for attributes are boolean , string , date and object . All non supported types will be passed a string . HTML attributes must use the data- prefix to be retrieved correctly. To pass JSON objects, you can use the JSONstringify Handlebars helper. If valid JSON, they will be returned as objects by the resolveAttributes() method. Example < my-custom-component data-my-string-param = \"Default value\" data-my-object-param = \"{{JSONstringify this 2}}\" data-my-date-param = \"01/01/2020\" data-my-boolean-param = \"true\" > ","title":"Create the component logic and sub components"},{"location":"extensibility/custom_web_component/#register-component-information","text":"The next step is to fill information about your new component. In the library main entry point (i.e. the class implementing the IExtensibilityLibrary in interface) return a new IComponentDefinition object in the getCustomWebComponents() method using these properties: Property Description componentName The name for your component. This name will be used as the custom HTML element name (ex: ). componentClass The web component class for that component. public getCustomWebComponents () : IComponentDefinition < any > [] { return [ { componentName : 'my-custom-component' , componentClass : MyCustomComponentWebComponent } ]; }","title":"Register component information"},{"location":"extensibility/custom_web_component/#consume-services-from-basewebcomponent","text":"const msGraphClientFactory = this . _serviceScope . consume < MSGraphClientFactory > ( MSGraphClientFactory . serviceKey ); const msGraphClient = await msGraphClientFactory . getClient ();","title":"Consume services from BaseWebComponent"},{"location":"extensibility/handlebars_customizations/","text":"Register Handlebars customizations \u00b6 By default, builtin helpers and open-source Handlebars helpers are available. If these don't fit your requirements, you can still create your own custom helper or partial that you can use in your HTML templates or layout fields (ex: 'Cards' or 'Details List' layouts). To avoid any conflict, each Web Part instance gets its own Handlebars isolated namespace (i.e. using Handlebars.create() ) meaning registering customizations in the global Handlebars namespace won't work (ex: using Handlebars.registerHelper() directly). To register a new Handlebars customization for the targeted Web Part (i.e. the Web Part instances where the extensibility library is registered and enabled): In the library main entry point (i.e. the class implementing the IExtensibilityLibrary in interface), register your customization using the registerHandlebarsCustomizations() method. The namespace parameter corresponds to the targeted Web Part Handlebars isolated namespace: From here, use the Handlebars API to add your customizations to this specific namespace. They will be availabe in templates for registered Web Part instances: public registerHandlebarsCustomizations ( namespace : typeof Handlebars ) { // Register custom Handlebars helpers // Usage {{myHelper 'value'}} namespace . registerHelper ( 'myHelper' , ( value : string ) => { return new namespace . SafeString ( value . toUpperCase ()); }); }","title":"Custom Handlebars customizations"},{"location":"extensibility/handlebars_customizations/#register-handlebars-customizations","text":"By default, builtin helpers and open-source Handlebars helpers are available. If these don't fit your requirements, you can still create your own custom helper or partial that you can use in your HTML templates or layout fields (ex: 'Cards' or 'Details List' layouts). To avoid any conflict, each Web Part instance gets its own Handlebars isolated namespace (i.e. using Handlebars.create() ) meaning registering customizations in the global Handlebars namespace won't work (ex: using Handlebars.registerHelper() directly). To register a new Handlebars customization for the targeted Web Part (i.e. the Web Part instances where the extensibility library is registered and enabled): In the library main entry point (i.e. the class implementing the IExtensibilityLibrary in interface), register your customization using the registerHandlebarsCustomizations() method. The namespace parameter corresponds to the targeted Web Part Handlebars isolated namespace: From here, use the Handlebars API to add your customizations to this specific namespace. They will be availabe in templates for registered Web Part instances: public registerHandlebarsCustomizations ( namespace : typeof Handlebars ) { // Register custom Handlebars helpers // Usage {{myHelper 'value'}} namespace . registerHelper ( 'myHelper' , ( value : string ) => { return new namespace . SafeString ( value . toUpperCase ()); }); }","title":"Register Handlebars customizations"},{"location":"extensibility/templating/","text":"Customize layout templates \u00b6 In a basic customization scenario, super users and webmasters can customize existing templates or start from a blank template to adapt the UI to their requirements. Templates can use either Handlebars or Adaptive cards templates to display data retrieved from the data source. Depdending of the template type, there are several options to customize a template: Handlebars Use regular HTML markup, Handlebars syntax and helpers . Write custom CSS styles . Adaptive cards Use declarative Adaptive Cards JSON templates with data. Both techniques Use data sources slots Use default web components provided by the solution. Use Microsoft Graph Toolkit components . Handlebars, HTML and CSS customizations \u00b6 The templates and fields HTML markup is sanitized automatically preventing XSS attacks. We used DOMPurify to do so. It means for instance, you cannot add your own