I have take reference for configurable product and followed below steps. Here may be some code is not usable.
1) Create app/code/Namespace/Modulename/etc/adminhtml/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<virtualType name="Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Pool">
<arguments>
<argument name="modifiers" xsi:type="array">
<item name="modulename" xsi:type="array">
<item name="class" xsi:type="string">Namespace\Modulename\Ui\DataProvider\Product\Modifier\Customtab</item>
<item name="sortOrder" xsi:type="number">200</item>
</item>
</argument>
</arguments>
</virtualType>
<type name="Namespace\Modulename\Ui\DataProvider\Product\Modifier\Customtab">
<arguments>
<argument name="formName" xsi:type="string">product_form</argument>
<argument name="dataScopeName" xsi:type="string">product_form.product_form</argument>
<argument name="dataSourceName" xsi:type="string">product_form.product_form_data_source</argument>
</arguments>
</type>
</config>
2) Create app/code/Namespace/Modulename/Ui/DataProvider/Product/Modifier/Customtab.php
<?php
namespace Namespace\Modulename\Ui\DataProvider\Product\Modifier;
use Magento\Catalog\Model\Locator\LocatorInterface;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Store\Api\WebsiteRepositoryInterface;
use Magento\Store\Api\GroupRepositoryInterface;
use Magento\Store\Api\StoreRepositoryInterface;
use Magento\Ui\Component\Form;
use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier;
class Customtab extends AbstractModifier
{
const SORT_ORDER = 40;
protected $locator;
protected $websiteRepository;
protected $groupRepository;
protected $storeRepository;
protected $websitesOptionsList;
protected $storeManager;
protected $websitesList;
private $dataScopeName;
public function __construct(
LocatorInterface $locator,
StoreManagerInterface $storeManager,
WebsiteRepositoryInterface $websiteRepository,
GroupRepositoryInterface $groupRepository,
StoreRepositoryInterface $storeRepository,
$dataScopeName
) {
$this->locator = $locator;
$this->storeManager = $storeManager;
$this->websiteRepository = $websiteRepository;
$this->groupRepository = $groupRepository;
$this->storeRepository = $storeRepository;
$this->dataScopeName = $dataScopeName;
}
public function modifyData(array $data)
{
return $data;
}
public function modifyMeta(array $meta)
{
if (!$this->storeManager->isSingleStoreMode()) {
$meta = array_replace_recursive(
$meta,
[
'tabname' => [
'arguments' => [
'data' => [
'config' => [
'additionalClasses' => 'admin__fieldset-product-customclass',
'label' => __('Your Label'),
'collapsible' => true,
'componentType' => Form\Fieldset::NAME,
'sortOrder' => $this->getNextGroupSortOrder(
$meta,
'search-engine-optimization',
self::SORT_ORDER
),
],
],
],
'children' => $this->getPanelChildren(),
],
]
);
}
return $meta;
}
protected function getPanelChildren()
{
return [
'tabname_products_button_set' => $this->getButtonSet()
];
}
protected function getButtonSet()
{
return [
'arguments' => [
'data' => [
'config' => [
'component' => 'Namespace_Modulename/js/components/container-tabname-handler',
'formElement' => 'container',
'componentType' => 'container',
'label' => false,
'content1' => __(
'Add some content'
),
'template' => 'ui/form/components/complex',
'createTabButton' => 'ns = ${ $.ns }, index = create_tabname_products_button',
],
],
],
'children' => [
'create_tabname_products_button' => [
'arguments' => [
'data' => [
'config' => [
'formElement' => 'container',
'componentType' => 'container',
'component' => 'Magento_Ui/js/form/components/button',
'actions' => [
[
'targetName' => $this->dataScopeName.'.customModal',
'actionName' => 'trigger',
'params' => ['active', true],
],
[
'targetName' => $this->dataScopeName.'.customModal',
'actionName' => 'openModal',
],
],
'title' => __('Your Title'),
'sortOrder' => 20,
],
],
],
],
],
];
}
}
3) Create app/code/Namespace/Modulename/view/adminhtml/layout/catalog_product_new.xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="admin-2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceBlock name="product_form">
<block name="product.form.modulename.matrix" class="Magento\ConfigurableProduct\Block\Adminhtml\Product\Edit\Tab\Variations\Config\Matrix" template="Namespace_Modulename::catalog/product/edit/tab/custom.phtml" as="product_custom">
<arguments>
<argument name="config" xsi:type="array">
<item name="collapsible" xsi:type="boolean">false</item>
<item name="label" xsi:type="string" translate="true" />
<item name="sortOrder" xsi:type="string">1000</item>
<item name="canShow" xsi:type="boolean">true</item>
<item name="componentType" xsi:type="string">fieldset</item>
<item name="provider" xsi:type="string">product_form.product_form_data_source</item>
<item name="form" xsi:type="string">product_form.product_form</item>
<item name="modal" xsi:type="string">customModal</item>
<item name="nameStepWizard" xsi:type="string">variation-steps-wizard</item>
<item name="dataScope" xsi:type="string">productFormCustommodule</item>
<item name="urlWizard" xsi:type="string">modulename/index/custom</item>
</argument>
</arguments>
</block>
</referenceBlock>
</body>
</page>
4) Create layout file for action app/code/Namespace/Modulename/view/adminhtml/layout/modulename_index_custom.xml
<?xml version="1.0" ?>
<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/layout_generic.xsd">
<container name="root" label="Root">
<block class="Namespace\Modulename\Block\Adminhtml\Catalog\Product\Edit\Tab\Custom" template="catalog/product/edit/tab/customfile.phtml" name="product_custom">
</block>
</container>
</layout>
5) Create block file app/code/Namespace/Modulename/Block/Adminhtml/Catalog/Product/Edit/Tab/Custom.php
<?php
namespace Namespace\Modulename\Block\Adminhtml\Catalog\Product\Edit\Tab;
class Custom extends \Magento\Backend\Block\Widget implements \Magento\Backend\Block\Widget\Tab\TabInterface
{
protected $_template = 'catalog/product/edit/tab/custom.phtml';
public function __construct(
\Magento\Backend\Block\Template\Context $context,
array $data = []
) {
parent::__construct($context, $data);
}
protected function _prepareLayout()
{
return parent::_prepareLayout();
}
}
6) Create controller file for action app/code/Namespace/Modulename/Controller/Adminhtml/Index/Custom.php
<?php
namespace Namespace\Modulename\Controller\Adminhtml\Index;
class Custom extends \Magento\Framework\App\Action\Action
{
protected $_resultPageFactory;
protected $resultJsonFactory;
protected $_errorHelper;
/**
* @param \Magento\Framework\App\Action\Context $context
* @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
* @param \Magento\Framework\Controller\Result\RawFactory $resultRawFactory
*/
public function __construct(
\Magento\Framework\App\Action\Context $context, \Magento\Framework\View\Result\PageFactory $resultPageFactory, \Magento\Framework\Controller\Result\RawFactory $resultRawFactory
) {
$this->_resultPageFactory = $resultPageFactory;
$this->resultRawFactory = $resultRawFactory;
parent::__construct($context);
}
public function execute()
{
$resultPage = $this->_resultPageFactory->create();
$block = $resultPage->getLayout()->getBlock('product_custom');
$this->getResponse()->appendBody($block->toHtml());
}
}
7) Create template file app/code/Namespace/Modulename/view/adminhtml/templates/catalog/product/edit/tab/custom.phtml
<div class="<?= /* @noEscape */ $block->getData('config/dataScope') ?>" data-role="step-wizard-dialog" data-bind="scope: '<?= /* @noEscape */ $block->getForm() ?>.<?= /* @noEscape */ $block->getModal() ?>'">
<!-- ko template: getTemplate() --><!-- /ko -->
</div>
<div class="<?= /* @noEscape */ $block->getData('config/dataScope') ?>" id="product-variations-matrix" data-role="product-variations-matrix">
<div data-bind="scope: 'configurableVariations'"></div>
</div>
<script type="text/x-magento-init">
{
"*": {
"Magento_Ui/js/core/app": {
"components": {
"<?= /* @noEscape */ $block->getData('config/form') ?>.<?= /* @noEscape */ $block->getModal() ?>": {
"component": "Namespace_Modulename/js/components/modal-custommodule",
"options": {"type": "slide", "title": "<?php echo $block->escapeHtml(__('Your Title')); ?>"},
"formName": "<?= /* @noEscape */ $block->getForm() ?>",
"isTemplate": false,
"stepWizard": "<?= /* @noEscape */ $block->getData('config/nameStepWizard') ?>",
"children": {
"wizard": {
"url": "<?= /* @noEscape */ $block->getUrl($block->getData('config/urlWizard'), ['id' => $block->getProduct()->getId()]) ?>",
"component": "Magento_Ui/js/form/components/html"
}
}
}
}
}
}
}
</script>
<script>
require(['jquery', 'mage/apply/main'], function ($, main) {
main.apply();
$('.<?= /* @noEscape */ $block->getData('config/dataScope') ?>[data-role=step-wizard-dialog]').applyBindings();
$('.<?= /* @noEscape */ $block->getData('config/dataScope') ?>[data-role=product-variations-matrix]').applyBindings();
})
</script>
8) Create my custom phtml file app/code/Namespace/Modulename/view/adminhtml/templates/catalog/product/edit/tab/customfile.phtml
and write my code here.
9) Create Js app/code/Namesapce/Modulename/view/adminhtml/web/js/components/modal-custommodule.js
define([
'Magento_Ui/js/modal/modal-component',
'uiRegistry',
'underscore'
], function (Modal, registry, _) {
'use strict';
return Modal.extend({
defaults: {
stepWizard: '',
modules: {
form: '${ $.formName }'
}
},
/**
* Open modal
*/
openModal: function () {
var stepWizard = {};
this.form().validate();
if (this.form().source.get('params.invalid') === false) {
stepWizard = registry.get('index = ' + this.stepWizard);
if (!_.isUndefined(stepWizard)) {
stepWizard.open();
}
this._super();
}
}
});
});
10 ) Create Js app/code/Namesapce/Modulename/view/adminhtml/web/js/components/container-tabname-handler.js
define([
'uiComponent'
], function (Element) {
'use strict';
return Element.extend({
defaults: {
listens: {
'${ $.provider }:data.is_downloadable': 'handleProductType'
},
links: {
isDownloadable: '${ $.provider }:data.is_downloadable'
},
modules: {
createConfigurableButton: '${$.createConfigurableButton}'
}
},
/**
* Invokes initialize method of parent class,
* contains initialization logic
*/
initialize: function () {
this._super();
this.handleProductType(this.isDownloadable);
return this;
},
/**
* Calls 'initObservable' of parent
*
* @returns {Object} Chainable.
*/
initObservable: function () {
this._super()
.observe(['content']);
return this;
},
/**
* Change content for container and visibility for button
*
* @param {String} isDownloadable
*/
handleProductType: function (isDownloadable) {
if (isDownloadable === '1') {
this.content(this.content2);
if (this.createConfigurableButton()) {
this.createConfigurableButton().visible(false);
}
} else {
this.content(this.content1);
if (this.createConfigurableButton()) {
this.createConfigurableButton().visible(true);
}
}
}
});
});
Best Answer
By default this functionality already exist in M2. You can check following file
In template file, there is an condition like
If this condition is true then you can able to view 'Move to Wishlist' under product image that means
Simple thought is if you login then you can able to view this button in cart page.
[Updated]
Following two function make the 'Move to Wishlist' button visible.
First logic implement in helper class.
Here $this->isAllow() return true if 'wishlist' module output is active(menas this module is not disabled from Admin->Stores->Configuration->Advanced) and and active this module too. $this->getCustomer() return true if login.